<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Off by one</title>
  <subtitle>Longform thoughts of @_off_by_one</subtitle>
  <link href="https://offbyone.us/feed.xml" rel="self"/>
  <link href="https://offbyone.us"/>
  <updated>2024-07-07T00:00:00Z</updated>
  <id>https://offbyone.us</id>
  <author>
    <name>Zeke Nierenberg</name>
  </author>
  
  <entry>
    <title>Div III: Empowerment software</title>
    <link href="/posts/div-iii/"/>
    <updated>2013-05-01T00:00:00Z</updated>
    <id>/posts/div-iii/</id>
    <content type="html">&lt;div class=&quot;bg-red-100 border-2 border-red-400 rounded-lg text-red-700 px-3 pb-0 my-3 mt-12 flex items-start gap-4&quot; role=&quot;alert&quot;&gt;
  &lt;div class=&quot;text-sm&quot;&gt;
  &lt;p&gt;The following is a summary of my 2013 senior thesis at Hampshire college. Many of the themes I identified at the time still resonate with me, so I decided to include it in my blog. Some of it I roll my eyes at, but that&#39;s OK.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;introduction&quot; tabindex=&quot;-1&quot;&gt;Introduction &lt;a class=&quot;direct-link&quot; href=&quot;#introduction&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Picking a Div III&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My Div III started twelve months ago. I was looking for a large-scale project to create or join. I was considering a lab-based biology research project. But, I felt that that sort of project wouldn&#39;t play to my strengths. I was attending a Biomath lecture series at Smith College. The purpose was to present the complex intersection of computational techniques and life sciences. It was also intended to encourage young people to work with computational and biological fields. At the time, I was TA-ing Organic Chemistry (it had been my third semester of chemistry in a row). I didn&#39;t want to be a chemist. I wanted to be a doctor, and I had to study chemistry to go to medical school. Nonetheless, chemistry was interesting, and it was becoming less intimidating than it had been the year before.&lt;/p&gt;
&lt;p&gt;I heard a lecture on Genetic Programming by &lt;a href=&quot;https://www.loom.com/share/90513745a019439293fe1fbf178d6dfd&quot;&gt;Lee Spector&lt;/a&gt;. The talk was fascinating. He described how one could create a computer system where programs evolved to solve problems. It had been my first formal introduction to GP. During the talk, I remembered a conversation I had had with my friend &lt;a href=&quot;https://www.linkedin.com/in/omri-ackley/&quot;&gt;Omri Bernstein&lt;/a&gt;. He was telling me about how he thought there was probably an interesting project that would combine Genetic Programming and Organic Chemistry. In the QA of Lee&#39;s talk, I raised my hand, &amp;quot;Has anyone ever combined GP and organic chemistry.&amp;quot; He said that he hadn&#39;t heard of anyone doing that, but suggested that I meet with him and talk about it.&lt;/p&gt;
&lt;p&gt;I considered doing a Div III in that subject area. GP and chemistry seemed to leverage my strength in computer programming in ways that an ambiguous biology project wouldn&#39;t.&lt;/p&gt;
&lt;p&gt;I met with Lee a few times, once with a Smith chemist, &lt;a href=&quot;https://www.smith.edu/people/david-gorin&quot;&gt;David Gorin&lt;/a&gt;. Lee described GP to Dave. Dave thought of a project that might work. I was seriously considering doing the project.&lt;/p&gt;
&lt;p&gt;Then, the biomath lecturer coordinator announced that students could apply for an &lt;a href=&quot;https://www.fivecolleges.edu/academics/biomathematical-sciences&quot;&gt;NSF-funded Biomathmatics fellowship&lt;/a&gt;. I decided to apply with this project. If I was accepted, I would make this my Div III. I was, and I did... at least at first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Summer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before the Biomath fellowship, I had arranged an internship at the &lt;a href=&quot;https://resuscitationscience.com/&quot;&gt;Center for Resuscitation Science&lt;/a&gt; at Beth Israel Deaconess Medical Center in Boston. Now that I had the fellowship, I would have to juggle both. But, I didn&#39;t yet know what my responsibilities at BIDMC were. Lee and the fellowship coordinator agreed that it would be OK if I were to travel to the 5 college area weekly for meetings. Luckily for me, the job at BIDMC was pretty rote. I had to do a lot of manual data entry. I had no ongoing projects. They were fine with me working 3 days per week. I worked Monday, Tuesday, and Wednesday. On Thursday I would take the bus to Hampshire and attend a meeting with Lee and his research students. I stayed on a friend&#39;s couch and bussed back the next day.&lt;/p&gt;
&lt;p&gt;With Lee&#39;s group, my first task was to build the data infrastructure that would allow for the chemistry GP project. I was dealing with data from all sorts of different sources, building a database that would be easily accessed by GP. This project is described in the ChemGP section. At the same time, I was doing very repetitive work copying data from one computer system to another in the hospital. I realized that I had the technical tools to improve the BIDMC computer systems used for research. But, no one at BIDMC was willing to entertain the idea. In August, I wrote an essay about my experiences and frustrations with clinical research. After many iterations, this piece will be published in The Scientist in May.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fall&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the fall, I was working on my Div III while doing three advanced learning activities. I was teaching Intro to Web Development, TAing Organic Chemistry II, and TAing Genetic Programming. The largest commitment by far was teaching Web Development. I planned the classes, reviewed student work, and met with students. TAing Orgo was less work, but still took time. I had weekly TA sessions, went to class, reviewed material, and met with struggling students for extra help. TAing Genetic Programming was my smallest commitment. I went to class and occasionally met with students who needed help.&lt;/p&gt;
&lt;p&gt;By the end of October, I had made some progress on my Div III project. I had built WebGP (described below) and interfaced ChemGP with it to do runs. It was around this time that I went to a Hackathon hosted by Communicate Health at UMass. My team produced CarrotStick (described below). We won the Hackathon. That project started to change my priorities. Building software that related to people was very exciting.&lt;/p&gt;
&lt;p&gt;A few weeks later, the team went to another Hackathon. This one was hosted by Mass General Hospital and MIT&#39;s Hacking Medicine group. The subject was Global Health. We built Trext (described below) and received a second-place prize. Trext became the largest part of my Div III. I worked on Trext full time (+ 50%) for several months.&lt;/p&gt;
&lt;p&gt;As the technical demands of Trext plateaued (while I waited for the business aspects caught up), I started working on Modfinder, a tool to help Hampshire students find housemates. The impetus behind the project was primarily to learn a new technology stack. The project is described below.&lt;/p&gt;
&lt;p&gt;Throughout my Div III, I collaborated with different individuals. On ChemGP, I worked with Omri Bernstein, especially in the beginning. The opinion essay I wrote was co-authored with Dr. Martina Steurer, MD, although I did the primary writing. On CarrotStick, I collaborated a lot with Kira McCoy, and somewhat with James Matheson and Amit Ringel. I worked on Modfinder with Seth Toles.&lt;/p&gt;
&lt;p&gt;My preferred technology stack for web apps has changed significantly over the course of the year, mostly on the server side. I started the year working primarily with PHP/MySQL. Now I prefer Node.js, MySQL, and MongoDB.&lt;/p&gt;
&lt;h2 id=&quot;webgp&quot; tabindex=&quot;-1&quot;&gt;webGP &lt;a class=&quot;direct-link&quot; href=&quot;#webgp&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zekenie/GPJS&quot;&gt;Go to Github project&lt;/a&gt;&lt;br /&gt;
~&lt;a href=&quot;http://stout.hampshire.edu/~zan09/webGP/&quot;&gt;Demo&lt;/a&gt;~&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;webGP is a web-based, javascript tool that allows anyone to do symbolic regression GP runs.&lt;/strong&gt;&lt;/p&gt;
&lt;iframe title=&quot;vimeo-player&quot; src=&quot;https://player.vimeo.com/video/51937862?h=d86024926d&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;h3 id=&quot;what-is-gp&quot; tabindex=&quot;-1&quot;&gt;What is GP &lt;a class=&quot;direct-link&quot; href=&quot;#what-is-gp&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Genetic programming is a technique that allows computers to write programs. The genetic programmer must first write a function that can evaluate program-candidates. This is called the fitness function. This function evaluates a candidate program and returns some form of score. The genetic programmer must also build a random code generator that can produce random programs. With these two components, one can build the first steps of a GP system by generating many random programs and evaluating them with the fitness function. Typically, none of the random programs come close to a good score, but some are better than others. Thus far, this is mildly interesting, but not powerful. In order to leverage principles of Darwinian evolution, programs must be able to &amp;quot;reproduce.&amp;quot;&lt;/p&gt;
&lt;p&gt;A simple genetic programming system is completed when programs can mutate and/or breed with one another. A mutate function takes a program and returns a modified version of that program. A crossover function takes two programs, removes a piece of one, and replaces it with a piece of the other. To run the finished GP system, generate a series of random programs. Rate their fitnesses. Pick some of the better programs and breed them to produce a new series. Each series is called a generation. If the system is working properly, the programs&#39; fitness should improve over time.&lt;/p&gt;
&lt;p&gt;Genetic programming is a deep and rich field. There are countless papers published on variations of each of the concepts explained above.&lt;/p&gt;
&lt;h3 id=&quot;what-is-symbolic-regression&quot; tabindex=&quot;-1&quot;&gt;What is Symbolic Regression &lt;a class=&quot;direct-link&quot; href=&quot;#what-is-symbolic-regression&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Symbolic regression is a GP technique that attempts mathematically explain data. In some ways, it is analogous to linear regression. In linear regression, two-dimensional (x, y) data is modeled by a linear function. Symbolic regression is similar except there are no requirements on the type of function and the data can be n-dimensional (x, y, z, ...). Functions are generated through GP. Symbolic regression is powerful when searching through complex data.&lt;/p&gt;
&lt;h3 id=&quot;motivation&quot; tabindex=&quot;-1&quot;&gt;Motivation &lt;a class=&quot;direct-link&quot; href=&quot;#motivation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As scientific tests become less expensive and more abundant, scientists are facing increasingly large datasets (&lt;a href=&quot;http://europepmc.org/abstract/MED/19620020&quot;&gt;source&lt;/a&gt;). Symbolic regression has potential as a tool for scientists to make sense of the mess they&#39;re in. But, it faces a problem. The barrier to entry is too high. This was the inspiration for building webGP. I wanted to give people a way to run GP on data they have sitting in an excel file or database.&lt;/p&gt;
&lt;h4 id=&quot;function-choice-and-gp-parameters&quot; tabindex=&quot;-1&quot;&gt;Function choice and GP parameters &lt;a class=&quot;direct-link&quot; href=&quot;#function-choice-and-gp-parameters&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Function choices and GP parameters. I made an interface that allows the user to decide what mathematical operations the evolved programs may employ. The user can also specify parameters about the run (its length, size of generations, etc).&lt;/p&gt;
&lt;h4 id=&quot;visualization-of-results&quot; tabindex=&quot;-1&quot;&gt;Visualization of results &lt;a class=&quot;direct-link&quot; href=&quot;#visualization-of-results&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Real time
&lt;ul&gt;
&lt;li&gt;Visualizing the best error in each generation as the data comes in&lt;/li&gt;
&lt;li&gt;Visualizing the best program&#39;s size as it is generated&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;After the run
&lt;ul&gt;
&lt;li&gt;Best program&#39;s predictions against fitness cases. A graph that plots two series. The fitness cases and the best program&#39;s prediction of what they should be. This gives the user a rough understanding of how good the best program is.&lt;/li&gt;
&lt;li&gt;Piano plot. This graph shows each individual program&#39;s score plotted on the y-axis and its (arbitrary) ID on the x-axis. The IDs are based on score and generation. Each point is shaded based on its length. This plot can give the user a sense of the diversity of the population in each generation&lt;/li&gt;
&lt;li&gt;Size vs error vs age. This graph shows how the relationship between size of programs, their error, and their generation. It gives the user a sense of how size and error changed over generation time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;process-of-building&quot; tabindex=&quot;-1&quot;&gt;Process of building &lt;a class=&quot;direct-link&quot; href=&quot;#process-of-building&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At first, I started building a simple GP engine in javascript as a way of learning GP. I had been working on other projects in Clojure, but didn&#39;t have a good feel of the language. I didn&#39;t want to let this technical clumsiness inhibit my learning of GP. Once I had the engine working, I realized how easy it would be to make a web front-end. First, I just made the data table and GP options. Then, I wanted to see how the process was going in real time. I started learning &lt;a href=&quot;http://d3js.org/&quot;&gt;D3.js&lt;/a&gt;, a graphing library. I made some classes for bar charts and scatterplots--the two types of visualization I eventually wanted. With those classes, I made some powerful graphs.&lt;/p&gt;
&lt;h3 id=&quot;challenges&quot; tabindex=&quot;-1&quot;&gt;Challenges &lt;a class=&quot;direct-link&quot; href=&quot;#challenges&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When webGP is used for small problems, it&#39;s great. Chrome&#39;s V8 javascript engine is lightning fast, and webGP is written well enough to harness its speed. But, because I wrote webGP to store and analyze every program ever produced by GP, it eats up a lot of RAM. If you have many generations, and each one is very large, it crashes the browser tab. Also, some of the visualization techniques performed after the run need to output a point for every program. This can take some time. I did make a modification to have the scatterplots only reflect a representative sample of the population. This improved speed considerably.&lt;/p&gt;
&lt;h3 id=&quot;what-would-come-next&quot; tabindex=&quot;-1&quot;&gt;What would come next &lt;a class=&quot;direct-link&quot; href=&quot;#what-would-come-next&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To mitigate the memory problem, I would send the programs to a &lt;a href=&quot;http://nodejs.org/&quot;&gt;node.js&lt;/a&gt; server to be stored. Node.js has the capacity for real-time client-server interaction with the use of websockets. This means once programs are created and evaluated, they can be stored elsewhere. This frees the client to run as long as it needs to. It also opens up the door for multiple computers all running the same problem.&lt;/p&gt;
&lt;h3 id=&quot;status&quot; tabindex=&quot;-1&quot;&gt;Status &lt;a class=&quot;direct-link&quot; href=&quot;#status&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This project is open source and on &lt;a href=&quot;https://github.com/zekenie/GPJS&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;chemgp&quot; tabindex=&quot;-1&quot;&gt;ChemGP &lt;a class=&quot;direct-link&quot; href=&quot;#chemgp&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zekenie/ChemGP&quot;&gt;&lt;img src=&quot;https://github.com/zekenie/ChemGP&quot; alt=&quot;Go to Github project&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/chemGP.png&quot; alt=&quot;ChemGP&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this project, I sought to find mathematical relationships between several attributes of molecules and their reaction rate constant in a cycloaddition reaction. I used symbolic regression GP in the search. Cycloadditions are reactions that make a new hydrocarbon ring (or &amp;quot;cycle&amp;quot;). They are synthetically powerful for many reasons, but here I will focus on their utility in &amp;quot;bioorthogonal&amp;quot; chemistry (BC). BC is the study of synthetic chemical reactions that run inside an organism but do not interfere with the endogenous biochemical reactions. Applications of BC include attaching fluorescent molecules to proteins without the standard transgenic mutations required for the technique. These &amp;quot;tagging&amp;quot; reactions could be used by biologists or clinicians for diagnostic purposes. Bioorthogonal chemistry might one day be used for therapeutic purposes.&lt;/p&gt;
&lt;p&gt;In order to be useful, BCs must adhere to a rather stringent set of requirements. They must react selectively (have little to no side reactions). They must not be cytotoxic (deadly to the tissue or organism). They must be kinetically accessible (they must happen quickly enough and in high enough yield to be measured). Many of the synthetic techniques used by organic chemists require there to be no water in the reaction. BC reactions must run in water. Organic chemists can control the temperature, pressure, and pH of their reactions, BC reactions must be under physiological conditions. These criteria make BC reactants hard to find.&lt;/p&gt;
&lt;p&gt;In this project, I used symbolic regression GP to search for a mathematical relationship between known properties of BC cycloadditions and their reaction rate constants (k). In order to start with GP, I had to build a significant amount of cheminformatic infrastructure. Essentially, I needed a database of reactions. I needed many parameters about each molecule in the reaction--two reactants, a solvent, and a product. I also needed information about the reaction (its pressure, temperature, reaction rate).&lt;/p&gt;
&lt;p&gt;I built the database with PHP/MySQL and javascript. I employed the &lt;a href=&quot;http://web.chemdoodle.com&quot;&gt;Chemdoodle web framework&lt;/a&gt;, an HTML 5/javascript chemistry toolkit. They have an API service which was purchased for chemical computations. Using Chemdoodle, I manually drew dozens of reactions from two papers and entered the reaction parameters. Chemdoodle&#39;s API calculated parameters for each molecule and I wrote code to save these calculations into MySQL.&lt;/p&gt;
&lt;p&gt;In order to get the data in a format that allowed webGP to be used, I needed to write code to translate the multidimensional MySQL data into a linear list. Each reaction consisted of four molecule rows, one reaction row, and several rows to indicate the relationship. Each row had up to 16 fields. I had to translate this information into a simple list. To do this I prefixed all molecule data with the role it was playing in the reaction (diene-molecular weight, dieneophile molecular weight, solvent molecular weight, etc). From this, I was able to create a simple table representing all the data. From this I used webGP to generate functions that took every reaction variable and generated the reaction rate constant.&lt;/p&gt;
&lt;p&gt;I found that the webGP engine that had worked well for simple problems was not effective with this problem. The programs evolved but plateaued early in the run (they did not continue to improve). In order to improve the GP performance, I patched webGP to include &amp;quot;&lt;a href=&quot;http://faculty.hampshire.edu/lspector/pubs/trivial-geography-toappear.pdf&quot;&gt;trivial geography&lt;/a&gt;&amp;quot; in the mating functions. This technique selects the most fit programs from each area in the population, not the population at large. This allows for slightly less fit programs to survive longer and preserves diversity in the population.&lt;/p&gt;
&lt;p&gt;The implementation of trivial geography made the runs&#39; plateaus come later, and their fitnesses improve.&lt;/p&gt;
&lt;p&gt;I also needed to modify the code to include separate data categories for training and validation of the programs. Certain reactions were randomly selected to train (be used in the fitness evaluation) while others were left to evaluate the fitness of the best programs from the run. Because these data were not used in the training, their incorporation reduces the degree to which the evolved programs are biased towards the dataset.&lt;/p&gt;
&lt;p&gt;I presented this project at a &lt;a href=&quot;http://4cbc.cs.umass.edu/&quot;&gt;4CBC&lt;/a&gt; event in November 2012. The slides from this presentation are available &lt;a href=&quot;p1113.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; This project is open source and on &lt;a href=&quot;https://github.com/zekenie/ChemGP&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;carrotstick&quot; tabindex=&quot;-1&quot;&gt;CarrotStick &lt;a class=&quot;direct-link&quot; href=&quot;#carrotstick&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/cs.png&quot; alt=&quot;CarrotStick&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Carrot Stick is a piece of web software written at a hackathon hosted by &lt;a href=&quot;http://communicatehealth.com&quot;&gt;Communicate Health&lt;/a&gt; last fall. We were tasked with writing software that helped communicate public health messages. A team of four (myself, &lt;a href=&quot;https://www.linkedin.com/in/james-matheson-1ba7b34/&quot;&gt;James Matheson&lt;/a&gt;, Kira McCoy, and Amit Ringel) worked for two days to produce Carrot Stick.&lt;/p&gt;
&lt;p&gt;The abstract from Kira McCoy&#39;s Div III:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Smoking is the leading cause of preventable death in the US (Danaei, Ding et al. 2009), and accounts for 1 in 10 deaths worldwide (Park, Tudiver et al. 2012). Information is not enough to change behavior. Many smoking cessation interventions have harnessed the power of social relationships, however none have provided direct messages in loved ones&#39; voices. The literature shows that &amp;quot;The person-to-person spread of smoking cessation appears to have been a factor in the population level decline in smoking seen in recent decades&amp;quot; (Christakis and Fowler 2008). People are connected, as is their health. Social relationships and expanded agency can be used to help people achieve health goals like smoking cessation. Services such as counseling may have short-term impacts on smoking habits, but concern from a loved one may have lasting effects.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;A feasibility study of the program &amp;quot;CarrotStick&amp;quot; was conducted to assess a novel smoking cessation intervention. CarrotStick empowers smokers to quit by providing unique support from family and friends. A smoker signs up online, invites friends to record messages and when she feels the urge, calls a number and hears either a &amp;quot;carrot message&amp;quot; (positive reinforcement) or a &amp;quot;stick&amp;quot; (tough love). Participants were smokers over the age of 18, who have the intention to quit. They were recruited through posters and online advertisements, leading them to the study website. Participants completed three surveys throughout the intervention, assessing smoking behavior, satisfaction with CarrotStick and other feasibility items.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The results being analyzed include assessing the number of recorded messages, types of messages, frequency of listening, attrition rates, impacts of reminders, software satisfaction, and limited smoking cessation efficacy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This study will provide insight into the impact and feasibility of implementing CarrotStick on a larger scale.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Carrot Stick was unanimously chosen by the judges to receive the top prize. Our group was elated. We were surprised by the degree to which our collaboration had improved our final product. We were all surprised by how good Carrot Stick looked after just two days.&lt;/p&gt;
&lt;p&gt;Kira decided to keep working. She changed her Div III. She wanted to run a feasibility study on real smokers to see if Carrot Stick really helped people. To do this, I had to do some ongoing work. I built out different views for the app for participants in the control group and participants in the treatment group. I also designed and developed a survey form that combined three tobacco dependence measurements that Kira found. Finally, after noticing that people forgot to call into Carrot Stick, I made an SMS reminder system.&lt;/p&gt;
&lt;p&gt;Carrot Stick was built with PHP, MySQL, the &lt;a href=&quot;http://www.twilio.com/voice&quot;&gt;Twilio voice API&lt;/a&gt;, &lt;a href=&quot;http://jquery.com&quot;&gt;jQuery&lt;/a&gt;, and &lt;a href=&quot;http://underscorejs.org/&quot;&gt;underscore.js&lt;/a&gt;. It was deployed on the &lt;a href=&quot;http://en.wikipedia.org/wiki/Platform_as_a_service&quot;&gt;PaaS&lt;/a&gt; &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt;. It was the first time I deployed on a PaaS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; The status of this project is unclear. We may continue development with grant money in the fall. If we don&#39;t, we will open-source the project.&lt;/p&gt;
&lt;h2 id=&quot;trext&quot; tabindex=&quot;-1&quot;&gt;Trext &lt;a class=&quot;direct-link&quot; href=&quot;#trext&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/zeke-md-pres.jpg&quot; alt=&quot;Trext&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After our group&#39;s success in the first Hackathon, we decided to try again. We heard about Hacking Medicine, an event at Massachusetts General Hospital hosted by the Massachusetts Institute of Technology. The event&#39;s theme was global health. They were looking for groups to build software or hardware that could have an impact globally.&lt;/p&gt;
&lt;p&gt;Before the event, we brainstormed for our idea. On our mind was the concept of the clinical checklist as presented by Dr. Atul Gawande, author of &lt;a href=&quot;http://www.thedailyshow.com/watch/wed-february-3-2010/atul-gawande&quot;&gt;The Checklist Manifesto&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dr. Gawande, a surgeon at Brigham and Women&#39;s hospital describes how two-minute checklists before certain procedures and operations can cut complications and death by double-digit percentages. As medicine&#39;s complexity has increased so have routine errors. Even experts with tens of thousands of hours of training make simple mistakes when confronted with complex, time-sensitive choices. Checklists can ensure that routine precautions are always observed in clinical settings.&lt;/p&gt;
&lt;p&gt;Also on our minds was the limited technology in many low and middle-income countries. Building a web app for checklists was tempting, but our target populations don&#39;t have reliable or fast internet. However, some technology has penetrated nearly the entire planet. Most people have access to cell phones with SMS, for example.&lt;/p&gt;
&lt;p&gt;We thought about building an SMS checklist builder. It would be great for data collection and updating checklists in a way that paper wouldn&#39;t. But, SMS is cumbersome, especially on older phones. One of Dr. Gawande&#39;s two-minute checklists might take 4 or 6 minutes by text. More importantly, some of the questions weren&#39;t relevant.&lt;/p&gt;
&lt;p&gt;We stumbled on the idea of using the format of a decision tree to build these checklists. Decision trees can achieve complexity with a short path by eliminating irrelevant questions. We envisioned a simple GUI that would allow basic computer users to build SMS text apps.&lt;/p&gt;
&lt;p&gt;At the event, we used a javascript library called JSPlumb as the backbone of our interface. On the server side, we used PHP and MySQL. The SMS functionality was achieved with the Twilio SMS API. It was an ambitious project for a two-day competition. But, 30 minutes before our presentation, we had a working prototype. For the presentation, we represented a clinical protocol for resuscitating infants. Our presentation could only be three minutes long so everything had to be condensed. We won one of the 2nd place prizes at the competition.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/trext-idea.jpg&quot; alt=&quot;Trext Idea&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The software 2 days after the competition:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/earlyTrext.png&quot; alt=&quot;Early Trext&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There was a palpable feeling that we were really good at this. We wanted to keep going. We thought the texting app was fantastic and we wanted to see it used on the ground in global health settings. But, everyone we showed it to had different ideas of use cases. Most of them were not in the field of global health. We realized we had built a useful design tool.&lt;/p&gt;
&lt;p&gt;We thought about using the tool we&#39;d created as a commercial product. But, before we could, we needed to rewrite the entire codebase. The hackathon code wasn&#39;t thought out very well and couldn&#39;t support new features. We wanted to use &lt;a href=&quot;http://nodejs.org&quot;&gt;Node.js&lt;/a&gt; on the server side because of some of its &lt;a href=&quot;http://loadimpact.com/blog/nodejs-vs-php-using-load-impact-to-visualize-nodejs-efficiency&quot;&gt;advantages over PHP&lt;/a&gt;. But, node is hard to use. We had a lot of interest in the software, and we didn&#39;t have time to learn an entirely new technology stack. So, we took on some &lt;a href=&quot;http://en.wikipedia.org/wiki/Technical_debt&quot;&gt;technical debt&lt;/a&gt;. We did a rewrite with the same technology stack. Much of the rewrite happened over winter break while I was shopping the idea around to VCs, talking to lawyers, and pitching to San Francisco-based leads.&lt;/p&gt;
&lt;p&gt;The team worked intensely over January. We worked on a batch texting, or &amp;quot;Phonebook&amp;quot; feature in which trees could be started by our clients not the end-users. But, one client wanted to be able to reference information about the end-user in trees. They wanted to say something like, &amp;quot;Hey Zeke, we haven&#39;t heard from you in a while, how&#39;s the [insert x y or z] going?&amp;quot; We thought about creating certain fields that we could know about each user (name, email, etc), but realized that our different client bases wanted radically different things. Creating rigid fields wouldn&#39;t be consistent with what we&#39;d already built: a design tool. We decided that any contact should be able to have n fields associated with this. We had been using the standard relational database: MySQL. We decided to employ a NoSQL environment without strict schemas.&lt;/p&gt;
&lt;p&gt;But, we were slightly stuck. We didn&#39;t know how to graphically display very abstract, open-ended data. The solution came when we realized how our users would actually import contact data: an excel file. Our users have existing lists of the people they want to text, we just need to give them a way to upload them and view that excel file online. Here&#39;s a snapshot of how the Phonebook works with the tree builder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/phonebook2.png&quot; alt=&quot;Phonebook&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/img/div/phonebook1.png&quot; alt=&quot;Phonebook&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In February, we refactored the code three times. The third refactor was separating the code that ran trees on phones with the code base that built trees. They are truly different programs and it made sense to run them in different environments so they could be scaled appropriately. (We also realize that the phonebook can and should be separated. That way we&#39;ll be able to expose it as a commercial API someday.) We also rewrote the way conversations were stored. We realized that storing them in MySQL required a lot of processing every time a tree log was rendered. We started storing the logs in MongoDB and cut processing time significantly. In this time we also designed a &lt;a href=&quot;http://trext.me&quot;&gt;marketing site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is a screenshot of the Trext builder today:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/trextToday.png&quot; alt=&quot;Trext Today&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;object width=&quot;560&quot; height=&quot;315&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/brUb9-aUiMM?hl=en_US&amp;amp;version=3&amp;amp;rel=0&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot; /&gt;&lt;embed src=&quot;http://www.youtube.com/v/brUb9-aUiMM?hl=en_US&amp;amp;version=3&amp;amp;rel=0&quot; type=&quot;application/x-shockwave-flash&quot; width=&quot;560&quot; height=&quot;315&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; /&gt;&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;We are currently working on rewriting the Trext SMS program in Node.js. The program that sends and receives SMSs is a perfect candidate for Node. Its IO intensive and would benefit from persistent connections to the database. Additionally, we may want to use websockets in the future to give real-time feedback about tree performance.&lt;/p&gt;
&lt;p&gt;The TrextNode project is fully functional, but less tested than the PHP version. After it has been tested and transitioned into use, we will rewrite the backend of the tree builder and web app in Node for a consistent technology stack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Future of Trext&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Trext is going to be a company. We have three investors that are going to support us for a time, and we hope to get enough revenue to be sustainable soon.&lt;/p&gt;
&lt;p&gt;Trext also has a future in global health and service. But, it is going to take some very different development. I don&#39;t want to get into business models, mostly because I&#39;m not a business person, but I know enough to say that the Trext service isn&#39;t affordable enough for global health organizations. We have fixed material costs somewhere between 1.5 and 2 cents per text message.&lt;/p&gt;
&lt;p&gt;Additionally, our text messages must come from the United States. This price and restriction makes no sense for a rural clinic in Haiti or Rwanda. However, I&#39;m working on a solution to these problems.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Mobile_broadband_modem&quot;&gt;USB GSM Modems&lt;/a&gt; (that are &lt;a href=&quot;http://www.traud.de/gsm/index.html&quot;&gt;AT command&lt;/a&gt; compatible) can send and receive text messages from computers. Node.js can send and receive data from USB ports. Theoretically, a computer could be configured as a trext server that does the texting internally. Technically, it wouldn&#39;t even need an internet connection, just cell service. If the computer were a laptop, reliable electricity wouldn&#39;t be necessary.&lt;/p&gt;
&lt;p&gt;To prototype the concept, I purchased a Samsung/Google Chromebook. I rigged it to run Ubuntu Linux from a standard SD camera card. I then configured the system with Git, Node.js, MySQL, and MongoDB. This process took a whole week, which was surprising. At every point in the configuration, I faced new and unexpected problems. I had never compiled C source code for example. I had cryptic errors all the time. When I ran the &#39;make&#39; command, I got &amp;quot;error (2).&amp;quot; Google was surprisingly unhelpful. Eventually, I found that the problem had been in the system&#39;s locale settings. Throughout the process, I got a lot better at manual system configuration and learned that package managers are my friend.&lt;/p&gt;
&lt;p&gt;Once I had the baseline system configured to my liking, I learned how to make a disk image with the dd command. I can now replicate the system onto new camera cards for new Chromebooks. The system is scalable. Its total cost is about $350. It runs Trext but could also work as a local web server for other services.&lt;/p&gt;
&lt;p&gt;I&#39;m currently working on adapting Trext to run on the system. I haven&#39;t yet purchased the GSM modem or begun that side of development, mostly due to cash flow problems. But I expect to be able to make the purchase next week.&lt;/p&gt;
&lt;p&gt;After graduation, I have plans to take two prototypes to Haiti and meet my friend Jean. Jean works for &lt;a href=&quot;http://www.oursoil.org/&quot;&gt;OurSoil&lt;/a&gt;. They want to test our system. They will use it for following up on trainings they run, sending out surveys, reminders, and educational information. If the test run goes well, we could easily replicate these systems and send them elsewhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Code is owned by Trext, Inc.&lt;/p&gt;
&lt;h2 id=&quot;modfinder&quot; tabindex=&quot;-1&quot;&gt;Modfinder &lt;a class=&quot;direct-link&quot; href=&quot;#modfinder&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://modfinder.me/&quot;&gt;Modfinder&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;./modfinder-source/index.html&quot;&gt;View Pretty Source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/modfinder1.png&quot; alt=&quot;Modfinder&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/img/div/modfinder2.png&quot; alt=&quot;Modfinder&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/img/div/modfinder3.png&quot; alt=&quot;Modfinder&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/div/modfinder4.png&quot; alt=&quot;Modfinder&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/img/div/modfinder5.png&quot; alt=&quot;Modfinder&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In February I went to &lt;a href=&quot;http://ecohacksf.org/&quot;&gt;EcoHackSF&lt;/a&gt; with &lt;a href=&quot;https://www.linkedin.com/in/sethtoles/&quot;&gt;Seth Toles&lt;/a&gt; and others. We built a cool toy, but nothing to showcase here. While we were there we talked a lot about web technologies. We both felt that we took shortcuts frequently while building websites. We wanted to &amp;quot;do it right&amp;quot; for once. We picked the most talked-about new web platform, Node.js. We decided to do a project in Node to learn it. After some brainstorming we decided to build a tool to help Hampshire students find mod-mates. We decided to model our service off the popular dating service &lt;a href=&quot;http://okcupid.com&quot;&gt;OK Cupid&lt;/a&gt;. OK Cupid works by letting users ask questions to the entire community and answer others&#39; questions. Users can answer the questions and also express the answer they accept in a partner. We outlined a plan to develop a similar platform. But, our platform would focus more on groups than on pairs. This is because group dynamics are much more complex than one-to-one relationships. You can think about an eight-person mod as a complete graph with eight nodes and 28 edges. There are 28 separate relationships in that mod. We wanted to do all we could to give people a good indicator about what they disagreed on.&lt;/p&gt;
&lt;p&gt;First, to see if there was interest, we put up a simple site explaining the idea. In 24 hours we had signups from 20% of the population that participates in the housing lottery. We considered this a success and built the service over spring break.&lt;/p&gt;
&lt;p&gt;Since the site&#39;s beta launch over spring break, we&#39;ve had ~234 users sign up, ~7000 questions answered, ~200 questions asked, and ~130 &amp;quot;favorites.&amp;quot; We&#39;ve made ~11,000 match calculations. All of these numbers are growing daily and will continue to do so until the housing lottery.&lt;/p&gt;
&lt;p&gt;We&#39;ve discovered that running a node app takes a lot of hands-on management. It frequently crashes and needs to be restarted. We&#39;ve been able to do a lot of work with catching exceptions which has prevented the worst of it. We&#39;ve also realized how immature the platform is. The PaaS that is built in node often has its own failures. We&#39;ve had pretty bad uptime, though it&#39;s gotten better recently.&lt;/p&gt;
&lt;p&gt;I&#39;m particularly proud of our matching algorithm which I will include here. I wrote the first pseudocode version, Seth rewrote it in proper Node.js. Then, I wrote the database integration.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;	_ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;underscore&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;	mysql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mysql&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;	connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mysql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[omitted]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;omitted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[omitted]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[omitted]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[omitted]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;multipleStatements&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; numMatches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SELECT * FROM `user` WHERE `numResponses` &gt; 5; SELECT * FROM `questionresponse`; SELECT * FROM `acceptableresponse`;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;results&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;//connection.destroy();&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;//connection.destroy();&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;exit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; results&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responses &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;results&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;userID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceptable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;results&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;userID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; userMatches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-----------&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;----------&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; overlap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intersection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;							_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pluck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responses&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;questionID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;							_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pluck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responses&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;questionID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;						&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;overlap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; points &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; possible &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;				_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;overlap&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;questionID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; importance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responses&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;questionID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;questionID&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;importance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					possible &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; importance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;						_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pluck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceptable&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;questionID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;questionID&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;answerID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;						_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responses&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;questionID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;questionID&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;answerID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;						points &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; importance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;						points &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; importance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;possible &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;					userMatches&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					numMatches&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;						&lt;span class=&quot;token string&quot;&gt;&quot;DELETE FROM `match` WHERE `userID` = ? AND `mUserID` = ? LIMIT 1; &#92;&lt;br /&gt;						INSERT LOW_PRIORITY INTO `match` (`userID`,`mUserID`,`match`,`base`) VALUES (?,?,?,?);&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;						&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;							i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;							i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;points &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; possible&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;possible &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;overlap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;br /&gt;						&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;						console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;br /&gt;					&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; match &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;points &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; possible&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;possible &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;					console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;===&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; : &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; j&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; : &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; match &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%, based on &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; overlap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;//possible: &quot; + parseInt(possible) + &quot;/&quot; + overlap.length);&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;UPDATE LOW_PRIORITY `user` SET `numMatches` = ? WHERE `userID` = ?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;userMatches&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userID&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userMatches&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; total matches.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numMatches&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;opinion-essay&quot; tabindex=&quot;-1&quot;&gt;Opinion Essay &lt;a class=&quot;direct-link&quot; href=&quot;#opinion-essay&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.the-scientist.com/researchers-hire-hackers-39392&quot;&gt;Published article&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Heroic investigations of life and disease have put us on the verge of a health-care revolution. Meanwhile, the software industry is booming; tech startups appear faster than you can say “medical informatics.” But at the intersection of these two complex fields lies dysfunction. Researchers and clinicians, shackled to software that no one in their right mind would use voluntarily, spend countless hours manually copying data from one system to another. And this separation of software development from clinical research is costing lives.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h3 id=&quot;juggling-data&quot; tabindex=&quot;-1&quot;&gt;Juggling data &lt;a class=&quot;direct-link&quot; href=&quot;#juggling-data&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;When paper records predominated in medicine, research assistants or medical students manually screened health records for certain diagnoses or laboratory values that would qualify patients for prospective clinical trials. Upon identifying suitable trial candidates and receiving their consent, researchers manually extracted the data of interest, recording the information on yet more paper. Retrospective cohort studies similarly took innumerable hours of searching health records in dusty basements. Data transfer and analyses followed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Now, the health-care system is in the process of turning away from paper records in favor of digital storage. Research assistants toil no more in dusty basements. Research workflow, however, is stuck in an earlier century. Today&#39;s research assistants still use their eyes to screen health records, but on computer monitors rather than paper. Once consent is obtained, relevant information is typically copied by hand from the digital records onto paper, then later recopied into digital research databases.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a recipe for bad data. Like the children&#39;s game &amp;quot;telephone,&amp;quot; in which a group whispers a sentence from one to another, the message can become garbled. In the past, this was unavoidable. Today, there is no excuse.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h3 id=&quot;time-points&quot; tabindex=&quot;-1&quot;&gt;Time points &lt;a class=&quot;direct-link&quot; href=&quot;#time-points&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;One consequence of this disorganization is inconsistency in when key measurements are taken. A hypothetical study might seek to determine if a specific protein level in patients&#39; blood 12 hours after arrival at the hospital is predictive of their outcome. Researchers would compile clinical data at this arbitrary time point, including vital signs, routine clinical laboratory values, and any study-specific measurements. The problem is that none of these data are collected at exactly 12 hours. Routine clinical measurements might be taken only in the morning, for example. Even the study-specific measurements, which the researchers control, may be off for logistical reasons. Such time differences are not routinely noted in the research database, resulting in the lowered statistical power of any conclusions to an unknown degree.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Even worse, important information is often lost: data that are repetitively measured in the clinical setting, like most vital signs, are only transferred to the research database at certain intervals; data measured at in-between times are not saved. This makes it impossible to look at specific trends that could lead to new types of data analysis.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;There is a better way&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If clinical researchers partnered with computer experts, we could transform the way data are collected, stored, and analyzed. We could reduce human error by mandating fewer manual steps, making research faster and data cleaner. Involving programmers in clinical research could also support more advanced data structures that would allow researchers to record infinite numbers of data points and display them in simple scatter plots to identify optimal time points. And perhaps most importantly, research assistants would be free to spend more time obtaining consent from patients, reviewing literature, and looking for trends in the data, instead of acting as human ethernet cables transferring information between different computer systems and paper records, and back again.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Relying more on computer systems would also make it easier for clinical researchers to find trial participants, by screening for certain lab values in a simple database query. All that is required is commonsense application programming interfaces (APIs), established protocols that allow different software platforms to communicate. In May 2011, realizing the emerging importance of these tools, President Obama directed every federal agency to have an API. Most clinical research teams do not have any APIs, nor the programming expertise to operate and maintain them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Finally, a more integrated system could also improve treatment. As the deluge of omics data continues, clinical researchers are becoming more and more dependent on computer software to make sense of it all. How can we gain insight from the 3 billion base pairs in the human genome when we can&#39;t effectively store and manipulate the 30-odd measures, such as blood pressure and heart rate, that we&#39;ve had for 50 years?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Writing a short essay is much harder than writing a paper for school. The hardest part for me was that I didn&#39;t know what I wanted out of it when I got started. I had something that was bothering me and I thought others should know about. In the summer, I saw that clinical researchers were using computers wrong. This had implications for everyone who&#39;s ever been touched by disease. When I was talking to my mom about it, she suggested I write an Op-Ed for the New York Times. My first draft was written with that intention. It was conversational, in the first person. I showed it to a lot of people and got generally positive feedback. People felt I should indeed try to get the message out. There was no consensus on the format.&lt;/p&gt;
&lt;p&gt;The New York Times wasn&#39;t interested in the piece. I submitted it to some medical journals: nothing. I sent it to a prior mentor: Dr. Ann Zovine. Ann is a physician researcher at UCSF. I worked for her the summer after the second year. She liked the piece and felt that it would be perfect for The Scientist.&lt;/p&gt;
&lt;p&gt;I submitted the essay to The Scientist. They didn&#39;t get back to me for a long time. School started. I was wrapped up in teaching my web development class and going to hackathons. This paper was on the back burner. Around winter break, they finally got back to me. They liked it, but they needed some credibility behind it. They wanted me to find a coauthor and strip out some of the personal narrative.&lt;/p&gt;
&lt;p&gt;I put out feelers and had a few people who were willing to work with me. One stood out. Dr. Martina Steurer-Muller is a pediatrics intensivist fellow at UCSF. She has a lot of experience with research and finds the computer tools totally lacking. She was happy to have her name on the paper.&lt;/p&gt;
&lt;p&gt;The editor at The Scientist was pleased that I had found someone. But, we needed to do a complete rewrite. This was really discouraging for me at first. I wasn&#39;t sure if the piece had any power without my personal experiences. The first drafts were very weak because I was trying to keep the same skeleton without any of what made it powerful. I didn&#39;t have anything good until I started from square one.&lt;/p&gt;
&lt;p&gt;Editing and revising this new paper with Martina and the editor took months. First, they were going to publish it only online in the April version. But then someone else at the magazine saw the piece, liked it, and decided it should be published in print in the May issue.&lt;/p&gt;
&lt;p&gt;The most surprising thing about this process for me was how long it took. I had this idea nearly a year ago and people are just going to start reading it now. It certainly makes blogging look attractive. I think the process produced a strong paper, but I think there were certain positive qualities lost from the first version of the paper. My takeaway from this is to choose the medium of publishing carefully.&lt;/p&gt;
&lt;h2 id=&quot;advocacy&quot; tabindex=&quot;-1&quot;&gt;Advocacy &lt;a class=&quot;direct-link&quot; href=&quot;#advocacy&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;An Example of Advocacy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve decided to include an email conversation I&#39;ve had with my high school Biology teacher.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zeke:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Can I ask your thoughts on an idea I&#39;ve been incubating? It&#39;s not something I&#39;m likely to do now, but it has to do with teaching kids to code and I think you might have some interesting reality checks for me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sure, ask away. I am happy to play the role of reality checker.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Zeke:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Problem #1: Good web development is too expensive. Programmers are in high demand and you have to pay top dollar to get a great-looking/working site/app. Most cannot afford this, so they use simple tools without much power.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Problem #2: We as a nation (planet?) aren&#39;t training programmers fast enough. This is one of the causes for problem #1. Programming can be taught effectively to children. It just isn&#39;t. In fact, teaching programming to kids has been anecdotally associated with higher test scores in math and science. Sometimes kids get so good at coding that they are ready for professional work in high school.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Solution to both: Let&#39;s think about a company. This company would make most of its money by doing web development contracts. We would bid 30% lower than the market, ensuring more reliable employment. We might even be in the position to pick and choose our clients. This part of the company would produce significant revenue because we&#39;d take on more projects than we could reasonably handle (hang on, I&#39;ll explain how this could ever work in a second).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The company would also interact with kids in middle school and high school. We&#39;d do development tutoring in very small groups charging hourly. This aspect of the business wouldn&#39;t be a cash cow either, but it would pay for itself. Kids who found they liked coding could go to an intensive development bootcamp over the summer. After the bootcamp, they would be eligible for apprenticeships. Apprentices would be paid $10 / hour and work a lot. These kids would also be the ones that loved it enough to go to the bootcamp. By this time, they&#39;d actually be pretty good. They&#39;d be working independently. Their mentors would review their work and help them get better, but they&#39;d actually be doing stuff. This is how the business model would work. These kids would be worth sooooo much more than $10 / hour. They&#39;d be doing client work that would get reviewed by the staff developers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This concept isn&#39;t that crazy. It&#39;s not unlike how doctors and lawyers are trained. The only thing that&#39;s different is the age group. I think this has the potential to shake up two industries at once: software and education.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Right now this is just a thought exercise for me. I&#39;d like to do it someday, but I&#39;m in no rush. I&#39;d love to hear your thoughts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi Zeke,&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Sorry it&#39;s taken me a while to get back to you, there&#39;s a lot going on at work at the moment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Before I get into a full discussion of your idea, I have a couple of clarifying questions:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;With regards to problem one, what is the penalty for not doing anything about this? Why does it matter that programmers are overvalued and most people use digital tools without much power? This is not meant to be entirely rhetorical&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;What is the current and projected demand for programmers? Where is this data coming from?&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I ask both of these first because you frame your idea as a solution. I want to make sure that we agree that there does, in fact, exist a problem; and it takes the form that you have described above.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;advocacy-1&quot; tabindex=&quot;-1&quot;&gt;Advocacy &lt;a class=&quot;direct-link&quot; href=&quot;#advocacy-1&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;An Example of Advocacy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve decided to include an email conversation I&#39;ve had with my high school Biology teacher.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zeke:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Can I ask your thoughts on an idea I&#39;ve been incubating? It&#39;s not something I&#39;m likely to do now, but it has to do with teaching kids to code and I think you might have some interesting reality checks for me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sure, ask away. I am happy to play the role of reality checker.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Zeke:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Problem #1: Good web development is too expensive. Programmers are in high demand and you have to pay top dollar to get a great-looking/working site/app. Most cannot afford this, so they use simple tools without much power.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Problem #2: We as a nation (planet?) aren&#39;t training programmers fast enough. This is one of the causes for problem #1. Programming can be taught effectively to children. It just isn&#39;t. In fact, teaching programming to kids has been anecdotally associated with higher test scores in math and science. Sometimes kids get so good at coding that they are ready for professional work in high school.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Solution to both: Let&#39;s think about a company. This company would make most of its money by doing web development contracts. We would bid 30% lower than the market, ensuring more reliable employment. We might even be in the position to pick and choose our clients. This part of the company would produce significant revenue because we&#39;d take on more projects than we could reasonably handle (hang on, I&#39;ll explain how this could ever work in a second).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The company would also interact with kids in middle school and high school. We&#39;d do development tutoring in very small groups charging hourly. This aspect of the business wouldn&#39;t be a cash cow either, but it would pay for itself. Kids who found they liked coding could go to an intensive development bootcamp over the summer. After the bootcamp, they would be eligible for apprenticeships. Apprentices would be paid $10 / hour and work a lot. These kids would also be the ones that loved it enough to go to the bootcamp. By this time, they&#39;d actually be pretty good. They&#39;d be working independently. Their mentors would review their work and help them get better, but they&#39;d actually be doing stuff. This is how the business model would work. These kids would be worth sooooo much more than $10 / hour. They&#39;d be doing client work that would get reviewed by the staff developers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This concept isn&#39;t that crazy. It&#39;s not unlike how doctors and lawyers are trained. The only thing that&#39;s different is the age group. I think this has the potential to shake up two industries at once: software and education.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Right now this is just a thought exercise for me. I&#39;d like to do it someday, but I&#39;m in no rush. I&#39;d love to hear your thoughts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hi Zeke,&lt;/p&gt;
&lt;p&gt;Sorry it&#39;s taken me a while to get back to you, there&#39;s a lot going on at work at the moment.&lt;/p&gt;
&lt;p&gt;Before I get into a full discussion of your idea, I have a couple of clarifying questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;With regards to problem one, what is the penalty for not doing anything about this? Why does it matter that programmers are overvalued and most people use digital tools without much power? This is not meant to be entirely rhetorical&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What is the current and projected demand for programmers? Where is this data coming from?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I ask both of these first because you frame your idea as a solution. I want to make sure that we agree that there does, in fact, exist a problem; and it takes the form that you have described above.&lt;/p&gt;
&lt;p&gt;I could give you my thoughts now, but that might be a little premature. Why don&#39;t you get back to me about those questions above and we can continue this dialogue&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zeke:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi Ben,&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Don’t worry about how long it took you to get back to me. I understand how busy life can be.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m not sure you see programming the way I do. Let me try to show you how I think about it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Language is an intrinsic property of humans; not a technology. We make languages like spiders make webs. Deaf twins in the absence of ASL education made their own sign language as their first language. They were on track (on track!) with their language development in terms of timing. Language is really a form of telepathy. I can put an idea in your head at my own will.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Writing on the other hand is different. It’s a technology. We developed it 5 or 6 thousand years ago as a way to make thoughts timeless, to transfer to others who weren’t there. It also had the added benefit of extending memory. People could write things down like how many chickens they had…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Writing is different from language because it extends humans’ natural abilities. If you put two kids together and don’t teach them, they will likely never be literate. It’s also much harder to learn as an adult. People have decided that this technology is so important that it was included in the UN Declaration of Human Rights.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;So:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Language: innate, way of transferring ideas&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Writing: technology, way of transferring ideas to different times, and remembering more&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;What are computer languages? Even if we didn’t have computers, computer languages would be useful on paper. They are very precise ways of writing down processes. They are specific and unforgiving. But, they completely describe a way of doing something that English doesn’t (intrinsically). The awesome thing about computers is that they can take this really specific language and DO IT FOR YOU! Every year we do more with computers. We communicate with them. We bank with them. We remember with them. We meet people. We even use them to think in ways that we aren’t inclined to, say, analyzing millions of records in a database…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Why are programmers in such high demand? There’s a computer in every office in much of the world. There are computers in most people’s pockets. We have this fantastic foundation for so much. But, very, very few know how to build.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m not saying we should replace learning to read with learning to code; that’s crazy. I’m just saying that it’s not even offered in most high schools. They have computer classes, just not coding classes. This is crazy. It’s also pretty crazy to insist that kids memorize the Pythagorean theorem, the Krebs cycle, and have no understanding of the backbone of modern math and science: computers. For more on this: &lt;a href=&quot;https://www.youtube.com/watch?v=60OVlfAUPJg&quot;&gt;Code.org Video&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Also check out this infographic that cites the Bureau of Labor Statistics, the College Board, and the Association for Computing Machinery: &lt;a href=&quot;https://code.org/promote&quot;&gt;Code.org Stats&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi Zeke,&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Thank you for explaining your thoughts in more detail. I think you make a compelling case for the importance of programming skills and the potential for integrating them into education more broadly. Here are my thoughts:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;I agree that there is a significant value in teaching programming to kids. It equips them with a new form of literacy that is increasingly relevant in our digital world. The analogy to language and writing is particularly striking.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Addressing the demand for programmers is indeed a critical issue. The data you’ve provided shows that the demand is growing and that we’re not keeping up with it. This mismatch creates opportunities for initiatives like the one you proposed.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Your idea of a company that combines web development services with educational programs is innovative. It leverages the high demand for programming skills to create a sustainable business model while also addressing an educational gap.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;One potential challenge is ensuring the quality of education provided to the students. You’d need a strong curriculum and experienced mentors to guide them effectively.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Another consideration is the scalability of the model. How would you ensure that the educational component can be scaled up to reach more students without compromising on quality?&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Overall, I think your idea has a lot of potential. It addresses real problems in both the software industry and education, and it could have a significant impact if executed well. I would be happy to continue discussing this and help you refine the concept further.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I predict that when we look back we will see the early 21st century as an unusual time. Computers have become commonplace. Most aspects of our lives have been transformed by their presence. Yet, most people don’t have the ability to harness their true power.&lt;/p&gt;
&lt;p&gt;Often organizations conclude that they can’t build their own computer systems. They hear about tech companies hiring hundreds of programmers and paying them well. But organizations can’t ignore computers either if they want to interact with the world. Organizations feel forced to use “canned goods”—computer tools that are supposed to “work” out of the box. I put “work” in quotes because they don’t really. They serve the lowest common denominator and take away many of the intrinsic benefits of computing. They don’t share data well. They don’t promote structured thinking (or data). One example of this is covered in detail in my opinion piece. I don’t feel the need to add more detail here.&lt;/p&gt;
&lt;p&gt;My Div III shows that there’s another way. I worked with (mostly) non-techies to build “empowerment software.” I showed that it’s possible to build web services that make a huge difference quickly and (relatively) cheaply.&lt;/p&gt;
&lt;p&gt;Much of the work done at tech companies is handling problems of scale. If Facebook was built to serve 1000 people, it would require a fraction of the development power, even with the same feature set. The problems these organizations face typically don’t need to be brought to millions. They can hire just one developer. If they did, a whole slew of new possibilities would be open to them—improving efficiency and quality of work.&lt;/p&gt;
&lt;p&gt;In fact, empowerment software is getting easier and easier. More and more services are open to small developers. Twilio is a great example of this. It lets someone with basic programming ability write web apps that can make and receive phone calls and text messages.&lt;/p&gt;
&lt;p&gt;But, more work is needed. There aren’t enough programmers. This makes even hiring one too expensive for many. If you ask me, something this important to society should be taught in every high school. At least basic competency should be a requirement. Changes like this would empower countless people and organizations to live better.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Await... async</title>
    <link href="/posts/await-async/"/>
    <updated>2015-11-15T00:00:00Z</updated>
    <id>/posts/await-async/</id>
    <content type="html">&lt;div class=&quot;video-wrapper&quot;&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/blem6Hfs4Yo&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>Sequelize ERD</title>
    <link href="/posts/sequelize-erd/"/>
    <updated>2016-01-18T00:00:00Z</updated>
    <id>/posts/sequelize-erd/</id>
    <content type="html">&lt;p&gt;ERD diagrams can be awesome. They visualize the relational graph of your application&#39;s data model. If your app is small enough, they can be a great piece of documentation. But, it can be hard to keep them up to date. That&#39;s why I made SequelizeERD. If you use &lt;a href=&quot;https://sequelize.org/&quot;&gt;Sequelize&lt;/a&gt;, plug it into &lt;a href=&quot;https://github.com/zekenie/sequelize-erd&quot;&gt;SequelizeERD&lt;/a&gt; and you&#39;ll automatically generate diagrams in whatever format you&#39;d like.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/zekenie/sequelize-erd/blob/release/sample.png?raw=true&quot; alt=&quot;Sample ERD&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;installation&quot; tabindex=&quot;-1&quot;&gt;Installation &lt;a class=&quot;direct-link&quot; href=&quot;#installation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You &lt;em&gt;don&#39;t&lt;/em&gt; need Graphviz or any non-javascript software to use this. Just&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; sequelize-erd --save-dev&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;api&quot; tabindex=&quot;-1&quot;&gt;API &lt;a class=&quot;direct-link&quot; href=&quot;#api&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Exported from &lt;code&gt;sequelize-erd&lt;/code&gt; is a function which takes your models. It can either take the &lt;code&gt;sequelize&lt;/code&gt; instance or a path to a file to require. The function returns an svg of the models.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; writeFileSync &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Sequelize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sequelize&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sequelizeErd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sequelize-erd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sequelize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Your Sequelize config object */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Import DB models here&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; svg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sequelizeErd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; db &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// sequelizeErd() returns a Promise&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./erd.svg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; svg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Writes erd.svg to local path with SVG file from your Sequelize models&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also customize the output format, engine, &lt;a href=&quot;https://graphviz.gitlab.io/_pages/doc/info/arrows.html&quot;&gt;arrow shapes&lt;/a&gt;, arrow size, &lt;a href=&quot;https://graphviz.gitlab.io/_pages/doc/info/colors.html&quot;&gt;line colors&lt;/a&gt;, and line width as well as include/exclude specific models.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; svg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sequelizeErd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// See available options below&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;circo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// See available options below&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;arrowShapes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Any of the below 4 options formatted [&#39;startShape&#39;, &#39;endShape&#39;]. If excluded, the default is used.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;BelongsToMany&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;crow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;crow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default: [&#39;none&#39;, &#39;crow&#39;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;BelongsTo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;inv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;crow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default: [&#39;crow&#39;, &#39;none&#39;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;HasMany&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;crow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;inv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default: [&#39;none&#39;, &#39;crow&#39;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;HasOne&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default: [&#39;none&#39;, &#39;none&#39;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;arrowSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default: 0.6&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;lineWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default: 0.75&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;green3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default: &#39;black&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;artist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;song&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;album&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;artistSong&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// sequelizeErd() returns a Promise&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./erd.svg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; svg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;from-bash&quot; tabindex=&quot;-1&quot;&gt;From bash &lt;a class=&quot;direct-link&quot; href=&quot;#from-bash&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;options&quot; tabindex=&quot;-1&quot;&gt;Options &lt;a class=&quot;direct-link&quot; href=&quot;#options&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;source&lt;/code&gt; relative path from project root to js file containing sequelize object with models loaded&lt;/li&gt;
&lt;li&gt;&lt;code&gt;destination&lt;/code&gt; Where you want your ERD SVG&lt;/li&gt;
&lt;li&gt;&lt;code&gt;include&lt;/code&gt; Only include the following models&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exclude&lt;/code&gt; Exclude the following models&lt;/li&gt;
&lt;li&gt;&lt;code&gt;format&lt;/code&gt; File format. Options are &amp;quot;svg&amp;quot;, &amp;quot;dot&amp;quot;, &amp;quot;xdot&amp;quot;, &amp;quot;plain&amp;quot;, &amp;quot;plain-ext&amp;quot;, &amp;quot;ps&amp;quot;, &amp;quot;ps2&amp;quot;, &amp;quot;json&amp;quot;, &amp;quot;json0&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;engine&lt;/code&gt; Layout engine to use, options are &amp;quot;circo&amp;quot;, &amp;quot;dot&amp;quot;, &amp;quot;fdp&amp;quot;, &amp;quot;neato&amp;quot;, &amp;quot;osage&amp;quot;, &amp;quot;twopi&amp;quot;. Default to &amp;quot;circo&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We expose a binary for you to use as a npm script. If you want an erd diagram generated in your project folder every time you commit, add this to your package json.&lt;/p&gt;
&lt;p&gt;The source path specifies a js file that must export your Sequelize DB instance. It also needs to load all your models.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;erd&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sequelize-erd --source ./path/to/your/sequelize/instance --destination ./erd.svg&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>We have coding in schools backwards</title>
    <link href="/posts/we-have-coding-in-schools-backwards/"/>
    <updated>2017-05-21T00:00:00Z</updated>
    <id>/posts/we-have-coding-in-schools-backwards/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;To hell with the job market: Kids should code because it helps them think.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;video-wrapper&quot;&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/qYZF6oIZtfc&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;The premise was simple. In 2013, If you can code, you&#39;ll be able to do things no one else can - be a wizard, or a rockstar. Different strokes for different folks - the point is Zuck can&#39;t hire enough engineers, so he really needs you to get started quickly.&lt;/p&gt;
&lt;p&gt;I have to admit, I was into it. &lt;em&gt;I&lt;/em&gt; learned to code as a kid. I started with webpages. With the help of mentorship, I was writing software by the time I started high school. Coding shaped how I thought about everything. I made a database driven website for vocabulary words in English. I made visual history timelines. I modeled physics homework in PHP (side-note: not the best tool for the job).&lt;/p&gt;
&lt;p&gt;The idea that coding was coming to the masses and changing education was (and is!) tantalizing.&lt;/p&gt;
&lt;p&gt;In the years since, my perspective has changed. The video has it wrong. We should indeed teach kids to code - but it&#39;s not about jobs - It&#39;s about thought.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Let&#39;s play a game. How many trade relationships are there between countries in the world? Let&#39;s say there are around 200 countries, and most countries have relationships with most other countries… how many could it be? 1000? 5000? Take a moment to guess - I&#39;ll wait.&lt;/p&gt;
&lt;p&gt;The actual number is close to 20,000 or 40,000, depending on if you count a bilateral relationship once or twice.&lt;/p&gt;
&lt;p&gt;A modern high school student writing a paper about trade would have no practical way to get a sense for the system of global trade. Sure, she could pull up facts about what the United States and China trade with each other. She could quote the White House and congressional Democrats on their opposing views of tariffs. But, she wouldn&#39;t be able to answer any of her own questions because of the information bottleneck of tens of thousands of data points.&lt;/p&gt;
&lt;p&gt;You may be thinking to yourself that this kind of sophisticated data analysis is beyond the grasp of a high school student - that an economist or statistician should be asking these questions. You may even be correct in 2019, but it doesn&#39;t have to be this way. These types of questions are within the intellectual grasp of young adults, and the tools exist to let them interact with them.&lt;/p&gt;
&lt;p&gt;It was for this reason that Steve Jobs called computation the Bicycle of the Mind. Computers makes it possible for everyone to play with information, answer their own questions, and discover things about the world.&lt;/p&gt;
&lt;p&gt;Let&#39;s play another game. Let&#39;s compare Drivers&#39; Ed. to actually driving a car for the first time.&lt;/p&gt;
&lt;p&gt;In Drivers&#39; Ed., students learn about the speed limit and the dangers of drinking. They learn about state laws and the consequences of breaking them. In a good class, they might learn a little bit about how cars work. Students do not learn how to drive.&lt;/p&gt;
&lt;p&gt;Learning how to drive a car is exploratory. You can literally take any road! It&#39;s a little scary. You need support and guidance, but at the end of the day, it&#39;s the student&#39;s job to try something, experience what happens, and learn from it.&lt;/p&gt;
&lt;p&gt;Today&#39;s conventional high schools are more like Drivers&#39; Ed. than driving. Students are asked to take in and regurgitate a tremendous amount of information - everything from the citric acid cycle to the Pythagorean theorem - in the hopes that this knowledge will expand their intellectual horizons. It even works, to an extent. It just doesn&#39;t come anywhere near its potential.&lt;/p&gt;
&lt;p&gt;We should be empowering students with the tools to become the next Pythagorus, or the next Albert Szent-Györgyi.&lt;/p&gt;
&lt;p&gt;Teaching the process of intellectual exploration is a lot harder than teaching facts. When you teach facts, you can expect a certain hegemony of outcomes. 35 students, 5 outcomes: A, B, C, D, F. Students knew the enzymes required for photosynthesis or they didn&#39;t. Student&#39;s could &amp;quot;do&amp;quot; the stoichiometry, or they couldn&#39;t.&lt;/p&gt;
&lt;p&gt;But did their minds expand?&lt;/p&gt;
&lt;p&gt;In an exploratory environment every student is expected to have their own unique outcome. The outcomes are hard to compare to one-another, but that&#39;s not really the point. Students probably don&#39;t have textbooks, workbooks, tests or homework. They have access to the raw tools, and a guide that can help them start to answer their own questions. Topics can be introduced. Teachers can help students get back on track by guiding them away from a line of inquiry, or critiquing how they used a certain tool.&lt;/p&gt;
&lt;p&gt;The value of inquiry-based learning is clear when you look at the macro outcomes. Citizens who have a fuzzy memory of how photosynthesis works don&#39;t change the world. People who discover the next Photosynthesis change the world.&lt;/p&gt;
&lt;p&gt;Of course knowledge is important. Of course facts are important. Hard skills are important, too. But, all knowledge comes from inquiry. All facts start as questions. Schools should be a place where students learn how to inquire, play, and create.&lt;/p&gt;
&lt;p&gt;There&#39;s no better tool to inquire, play, and create than the computer.&lt;/p&gt;
&lt;p&gt;What would STEM education look like if the ability to program a computer was a given?&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Offer scorecard</title>
    <link href="/posts/offer-scorecard/"/>
    <updated>2018-11-15T00:00:00Z</updated>
    <id>/posts/offer-scorecard/</id>
    <content type="html">&lt;div class=&quot;video-wrapper&quot;&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/M1xmxDJIgkU&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Link to model: &lt;a href=&quot;https://docs.google.com/spreadsheets/d/1Eo_PYHVULAmwdJ-Agj8EX1qoXpveemtIb-6CkYlyVYA/edit?usp=sharing&quot;&gt;model discussed in talk&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Pandemic writing practice</title>
    <link href="/posts/pandemic-writing-practice/"/>
    <updated>2020-08-29T00:00:00Z</updated>
    <id>/posts/pandemic-writing-practice/</id>
    <content type="html">&lt;p&gt;Living through a pandemic is rough. I&#39;ve been thinking about how I spend a lot of time consuming content. After work, I sit on the couch and look at netflix, tiktok, twitter... I refresh the new york times and washington post. I look at hackernews. I&#39;m chasing that dopamine hit.&lt;/p&gt;
&lt;p&gt;The brain is a plastic organ, changing over time in response to stimulation. I really enjoy when I&#39;m thinking actively. I enjoy producing. But, just like with my body and exercise, when I don&#39;t do it enough... it gets harder.&lt;/p&gt;
&lt;p&gt;Don&#39;t get me wrong, I spend a lot of time thinking. My job is cognitively demanding. But, I feel like I&#39;m not being as stimulated as I could be across the board.&lt;/p&gt;
&lt;p&gt;To break this down, I&#39;d like to start by exploring input to output ratios of various activities. In this exercise, I think it&#39;s important to try not to judge myself or others. It&#39;s hard. I probably will. When I write the word &amp;quot;TV,&amp;quot; I get into a very judge-y, finger-wag-y headspace. I&#39;m not sure that&#39;s productive.&lt;/p&gt;
&lt;p&gt;Input: like stdin. It&#39;s things I take in from the world&lt;/p&gt;
&lt;p&gt;Output: like stdout. Talking, writing, drawing, even moving maybe?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/writing-charts/OI-Ratio-Examples.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&#39;d like to look at these few example activities with another axis: &amp;quot;CPU load.&amp;quot; How much do they make me think?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/writing-charts/OI-Ratio-vs-CPU-Load.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I want to talk about how I spend a typical work day since COVID.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wake up&lt;/li&gt;
&lt;li&gt;feed the cats&lt;/li&gt;
&lt;li&gt;turn on NPR&lt;/li&gt;
&lt;li&gt;have 2 espresso&lt;/li&gt;
&lt;li&gt;read all news sites&lt;/li&gt;
&lt;li&gt;Try to get revved up for the day... try to see it as exciting, try to understand what&#39;s going to happen.&lt;/li&gt;
&lt;li&gt;This is easiest when I have a feature i&#39;m excited about or a deadline. It&#39;s hard when there&#39;s a lot of meetings or i feel like i&#39;m trudging through something exhausting&lt;/li&gt;
&lt;li&gt;Start working&lt;/li&gt;
&lt;li&gt;Having meetings&lt;/li&gt;
&lt;li&gt;Do more work&lt;/li&gt;
&lt;li&gt;Look at twitter&lt;/li&gt;
&lt;li&gt;take a walk&lt;/li&gt;
&lt;li&gt;At some point lunch&lt;/li&gt;
&lt;li&gt;More work&lt;/li&gt;
&lt;li&gt;twitter&lt;/li&gt;
&lt;li&gt;More meetings&lt;/li&gt;
&lt;li&gt;At some point work stops.&lt;/li&gt;
&lt;li&gt;Start thinking about dinner&lt;/li&gt;
&lt;li&gt;Watch some tv&lt;/li&gt;
&lt;li&gt;cook sometimes&lt;/li&gt;
&lt;li&gt;watch some tv&lt;/li&gt;
&lt;li&gt;twitter&lt;/li&gt;
&lt;li&gt;20 min on the exercise bike while watching tv&lt;/li&gt;
&lt;li&gt;shower&lt;/li&gt;
&lt;li&gt;tiktok&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/img/writing-charts/typical-day.png&quot; alt=&quot;adf&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Aside about noticing gaps here&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/writing-charts/Observation.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
&lt;em&gt;An interesting observation about the chart above... By my own assessment, I don&#39;t really have any high output mindless activities... I&#39;m not sure what they would or could be....&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What&#39;s getting me motivated to make changes in my life is all the &amp;quot;low CPU&amp;quot; input tasks my brain is doing after work. Aside from talking to claire over dinner, I feel like I&#39;m &amp;quot;vegging out.&amp;quot; It has a very brain death feeling&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. Tweet by tweet. Each requiring no critical thought.&lt;/p&gt;
&lt;p&gt;I also think it&#39;d be nice to find things to do when taking breaks... I feel like i&#39;m doing a lot of ADD-esq refreshing of COVID numbers.&lt;/p&gt;
&lt;p&gt;It&#39;s not like I think you should be outputting all the time... It&#39;s not like I think you should be in deep number crunching all the time. I think you need a healthy balance! I think it&#39;s good to have time to rest. But, I think my outlet for processing stress has been to consume massive amounts of thoughtless media. I&#39;d like to change that.&lt;/p&gt;
&lt;p&gt;So what am I going to do? Well, for one, I&#39;d like to start a writing practice. I want to write every day. Not something for others to read. Just to get some output. I think it&#39;ll help me think, but I also think it&#39;ll be good for my brain. Just like exercise. I&#39;d also like to read more... we&#39;ll see where that goes.&lt;/p&gt;
&lt;p&gt;I&#39;d like to have a dashboard. It can be spreadsheet for now. It just tracks if I&#39;m doing OK with exercise and writing... or maybe sleep, exercise, healthy balance of IO and CPU load.&lt;/p&gt;
&lt;p&gt;I think i need to find some passive input that is emotionally salient too. Like a good fiction book to dig into. Something to do before bed that has me thinking, retaining state&lt;/p&gt;
&lt;p&gt;Another thing I&#39;d like to discuss is that there are times where there is no input and no output but there&#39;s still CPU activity or memory access. Think about a long walk where you&#39;re thinking through something. You&#39;re not producing. You&#39;re not consuming. Or maybe you are and the read and write streams are just pointed at your brain.... I think that time is important to. I don&#39;t make enough time for it. All of this activity ultimately comes from that kind of time&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://search.proquest.com/openview/3adfa9041a519328a7567a99fe8e2193/1?pq-origsite=gscholar&amp;amp;cbl=2041939&quot;&gt;people who watch more hours of TV have faster cognitive decline&lt;/a&gt; &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Using Vega Lite to explore chemistry</title>
    <link href="/posts/using-vega-lite-to-explore-chemistry/"/>
    <updated>2021-02-28T00:00:00Z</updated>
    <id>/posts/using-vega-lite-to-explore-chemistry/</id>
    <content type="html">&lt;div id=&quot;observablehq-96a529c2&quot;&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://observablehq.com/@zekenie/using-vega-lite-to-explore-chemistry&quot;&gt;Check out this post on Observable&lt;/a&gt;&lt;/p&gt;
&lt;script type=&quot;module&quot;&gt;
import {Runtime, Inspector} from &quot;https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js&quot;;
import define from &quot;https://api.observablehq.com/@zekenie/using-vega-lite-to-explore-chemistry.js?v=3&quot;;
new Runtime().module(define, Inspector.into(&quot;#observablehq-96a529c2&quot;));
&lt;/script&gt;
</content>
  </entry>
  
  <entry>
    <title>Playing with Hilbert</title>
    <link href="/posts/playing-with-hilbert/"/>
    <updated>2021-03-13T00:00:00Z</updated>
    <id>/posts/playing-with-hilbert/</id>
    <content type="html">&lt;div id=&quot;observablehq-84f0926d&quot;&gt;&lt;/div&gt;
&lt;p&gt;Credit: &lt;a href=&quot;https://observablehq.com/@zekenie/playing-with-hilbert&quot;&gt;Playing with Hilbert by Zeke Nierenberg&lt;/a&gt;&lt;/p&gt;
&lt;script type=&quot;module&quot;&gt;
import {Runtime, Inspector} from &quot;https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js&quot;;
import define from &quot;https://api.observablehq.com/@zekenie/playing-with-hilbert.js?v=3&quot;;
new Runtime().module(define, Inspector.into(&quot;#observablehq-84f0926d&quot;));
&lt;/script&gt;
</content>
  </entry>
  
  <entry>
    <title>Cost of computing</title>
    <link href="/posts/cost-of-computing/"/>
    <updated>2021-10-29T00:00:00Z</updated>
    <id>/posts/cost-of-computing/</id>
    <content type="html">&lt;p&gt;Before we talk about the cost of computing, let&#39;s talk about the cost of computers.&lt;/p&gt;
&lt;p&gt;I did a &lt;a href=&quot;https://claude.ai/share/66310419-2610-476d-919e-87278749609a&quot;&gt;comical estimation with Claude&lt;/a&gt; on the cost (in todays dollars) for the CPU time that would have been required to load a web page in 1960 if the web had existed. It was $3,800. Today the same web page can be rendered for $0.00000006.&lt;/p&gt;
&lt;div class=&quot;content-wide-scaled&quot;&gt;
    &lt;div class=&quot;grid gap-8 grid-cols-1 lg:grid-cols-12  pointer-events-none&quot;&gt;
        &lt;div&gt;&lt;/div&gt;
    &lt;div class=&quot;transform lg:-rotate-2 lg:col-span-4&quot;&gt;
        &lt;img class=&quot;border rounded shadow lg:object-cover w-full&quot; src=&quot;/img/cost-of-computing/old.jpg&quot; /&gt;
        &lt;div style=&quot;font-size: 9px;&quot; class=&quot;text-gray-600&quot;&gt;&lt;span class=&quot;font-bold&quot;&gt;1957&lt;/span&gt;: The first municipal computer (Norwich, UK). Just one of 21 cabinets making up the computer.&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;lg:col-span-6 lg:mt-16 transform lg:rotate-2&quot;&gt;
        &lt;div style=&quot;font-size: 9px;&quot; class=&quot;text-gray-600&quot;&gt;&lt;span class=&quot;font-bold&quot;&gt;2015&lt;/span&gt;: The Raspberry Pi Zero&lt;/div&gt;
        &lt;img class=&quot;border shadow rounded lg:object-cover w-full&quot; src=&quot;/img/cost-of-computing/pi.png&quot; /&gt;
    &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;p class=&quot;text-sm text-gray-600 italic z-10&quot;&gt;
Credit to &lt;a target=&quot;_blank&quot; href=&quot;https://homepages.cwi.nl/~steven/Talks/2017/11-09-aarhus/&quot;&gt;Steven Pemberton&lt;/a&gt; for this comparison
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;But, it&#39;s not just computers. It would have cost more than $10k (in today&#39;s dollars) to light your home for a day several hundred years ago.&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;content-wide-scaled grid gap-2 grid-cols-1 sm:grid-cols-2 pointer-events-none&quot;&gt;
  &lt;iframe class=&quot;pointer-events-auto shadow&quot; src=&quot;https://ourworldindata.org/grapher/the-price-for-lighting-per-million-lumen-hours-in-the-uk-in-british-pound?yScale=log&amp;time=1301..latest&quot; loading=&quot;lazy&quot; style=&quot;width: 100%; height: 600px; border: 0px none;&quot;&gt;&lt;/iframe&gt;
  &lt;div&gt;
    &lt;img class=&quot;border p-2 shadow&quot; style=&quot;margin: 0&quot; src=&quot;https://ourworldindata.org/uploads/2020/11/solar-pv-prices-vs-cumulative-capacity.png&quot; /&gt;
    &lt;img class=&quot;shadow&quot; style=&quot;margin-top: 8px&quot; src=&quot;/img/cost-of-computing/genome.jpg&quot; /&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;We accept that prices change throughout history, but the sheer magnitude of the decline in technology prices is staggering. People in general are not great at contemplating non-linear changes&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Experience_curve_effects&quot;&gt;Experience curve effect&lt;/a&gt; is a pattern that shows up repeatedly in technology. As people learn, they get more efficient. They drive down errors by improving processes. Supply chains are optimized. Then, second order effects start to happen. As prices drop, markets grow. Purchasers who were recently priced out are start buying. That allows economies of scale come into play.&lt;/p&gt;
&lt;p&gt;Efficiencies compound. If you take a nap for a decade or two, numbers can get very very 🤏. Entrenched market dynamics that were once a given become a historical footnote. For example, it&#39;s not uncommon to hear people marvel at the fact that computers used to take up entire floors of high rises. Go to cocktail parties and start listening for the word &amp;quot;before.&amp;quot; The internet &lt;em&gt;before&lt;/em&gt; the web. Personal computers &lt;em&gt;before&lt;/em&gt; GUIs. Solar. Light. DNA Sequencing. Aviation. Automobiles. Batteries. The list goes on and on.&lt;/p&gt;
&lt;h3 id=&quot;inflection-points&quot; tabindex=&quot;-1&quot;&gt;Inflection points &lt;a class=&quot;direct-link&quot; href=&quot;#inflection-points&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Experience curves follow a nice predictable path until they don&#39;t. At some point the curve breaks. And when it does, it happens in one of two ways.&lt;/p&gt;
&lt;div class=&quot;content-wide-scaled&quot;&gt;
    &lt;img style=&quot;margin: 0; background: linear-gradient(135deg, #f5f7fa 0%, #fcffff 100%);&quot; class=&quot;w-full rounded border shadow&quot; src=&quot;/img/cost-of-computing/end-of-learning-curve.svg&quot; /&gt;
&lt;/div&gt;
&lt;h4 id=&quot;plateaus&quot; tabindex=&quot;-1&quot;&gt;Plateaus &lt;a class=&quot;direct-link&quot; href=&quot;#plateaus&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Plateaus occur when some kind of local minima is reached. For example, people have been saying we&#39;re almost at the end of Moore&#39;s law for my entire life. Even Gordon Moore &lt;a href=&quot;https://en.wikipedia.org/wiki/Moore&#39;s_law#Forecasts_and_roadmaps&quot;&gt;predicted&lt;/a&gt; that Moore&#39;s law would end at some point. At some point the laws of physics impose their limits. You can&#39;t really make a transistor smaller than an atom.&lt;/p&gt;
&lt;h4 id=&quot;&amp;quot;earthquakes&amp;quot;&quot; tabindex=&quot;-1&quot;&gt;&amp;quot;Earthquakes&amp;quot; &lt;a class=&quot;direct-link&quot; href=&quot;#&amp;quot;earthquakes&amp;quot;&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Sometimes the set of forced governing the experience curve suddenly change. This could be a technological innovation, government intervention, or even ecological or economic or cultural change. Whatever the cause, when a new dynamic takes effect, prices can change even more suddenly than an Experience Curve.&lt;/p&gt;
&lt;p&gt;In the case of whole genome sequencing, an inflection point occurred when &lt;a href=&quot;https://en.wikipedia.org/wiki/Massive_parallel_sequencing&quot;&gt;Massive Parallel Sequencing&lt;/a&gt; replaced &lt;a href=&quot;https://en.wikipedia.org/wiki/Sanger_sequencing&quot;&gt;Sanger Sequencing&lt;/a&gt;. It took this technology years to get to market, but once it did, the Experience Curve broke and a new one formed.&lt;/p&gt;
&lt;p&gt;It&#39;s hard to appreciate what this can mean for society. Every time we pay for something we&#39;re deciding &lt;em&gt;not&lt;/em&gt; to pay for something else of the same price.&lt;/p&gt;
&lt;p&gt;I &lt;a href=&quot;https://www.perplexity.ai/search/i-want-you-to-give-me-a-markdo-IMu9yerLQjuN9hQbGvgjQA&quot;&gt;asked Perplexity&lt;/a&gt; to help me come up with something else society could have done with the money it would cost to sequence one person&#39;s genome over the last 24 years.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;DNA Sequencing Price/ person&lt;/th&gt;
&lt;th&gt;Something else society could have done with the money&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2001&lt;/td&gt;
&lt;td&gt;$100,000,000&lt;/td&gt;
&lt;td&gt;Expansion of a (non-US) public transit system (e.g., Metro de Los Teques in Venezuela)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2006&lt;/td&gt;
&lt;td&gt;$10,000,000&lt;/td&gt;
&lt;td&gt;Renovation of a public library branch&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;$1,000,000&lt;/td&gt;
&lt;td&gt;Installation of a traffic management system&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2011&lt;/td&gt;
&lt;td&gt;$10,000&lt;/td&gt;
&lt;td&gt;Purchase of a police patrol vehicle&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;$4,000&lt;/td&gt;
&lt;td&gt;Installation of playground equipment&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;$1,000&lt;/td&gt;
&lt;td&gt;Purchase of a commercial-grade AED&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Different things are possible when something costs $10^8 than when they cost $10^3.&lt;/p&gt;
&lt;h3 id=&quot;what-about-the-cost-of-writing-programs&quot; tabindex=&quot;-1&quot;&gt;What about the cost of writing programs? &lt;a class=&quot;direct-link&quot; href=&quot;#what-about-the-cost-of-writing-programs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When I say &amp;quot;cost of  computing,&amp;quot; what I mean is the units of effort are required to express one unit of computational thought to a computer. We can sort of think of that as a price... And in many ways it is. Sometimes it&#39;s a cost in literal ways: it&#39;s an input to the cost structure of any organization employing software engineers. Sometimes it&#39;s a cost in less literal ways. When the cost of expressing computational thought changes, the ambitions of my side projects change because it&#39;s suddenly possible to do more.&lt;/p&gt;
&lt;p&gt;For the last couple decades, while I&#39;ve been a programmer, and until quite recently, the cost of computing has been in a plateau. Programming is different than it was in 2003. Programming is better than it was in 2003! But, (again until recently!), programming is not &lt;em&gt;cheeper&lt;/em&gt; than it was in 2003, in my opinion.&lt;/p&gt;
&lt;p&gt;But, historically, we know that at one point writing programs used to be more expensive than it is today. There was a time, for example, when programs were written on &lt;a href=&quot;https://en.wikipedia.org/wiki/Punched_card&quot;&gt;punch cards&lt;/a&gt;! For ages programs were expressed in &lt;a href=&quot;https://en.wikipedia.org/wiki/Assembly_language&quot;&gt;assembly&lt;/a&gt;. It&#39;s unclear to me when the price started to stagnate.&lt;/p&gt;
&lt;p&gt;The predominant UI for programming has been a bunch of text files in folders that get compiled down to something deeper. It&#39;s been a long time since a really radical programming idea has gained wide-spread adoption and created a new dynamic. I&#39;m not talking about some neat programming language. I&#39;m talking about a different way of thinking about what a program even is. One I can think of is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Spreadsheet&quot;&gt;Spreadsheet&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;content-full shadow-lg grid grid-cols-1 gap-0 md:grid-cols-2&quot;&gt;
  &lt;visicalc-component&gt;&lt;/visicalc-component&gt;
    &lt;spreadsheet-cell-focus&gt;&lt;/spreadsheet-cell-focus&gt;
&lt;/div&gt;
&lt;br /&gt;
Have you ever seen a spreadsheet expert at work? It&#39;s magical. Spreadsheets–when leveraged to their full potential–let the programmer construct elaborate computational models. They&#39;re live and reactive, not text files. Data visualization is built in. The learning curve is smooth. I think that a smart novice could start with recording tabular data and get them self to Monty Carlo simulations in about a month.
&lt;p&gt;And they took over every industry. &lt;em&gt;Every&lt;/em&gt; organization I&#39;ve interacted with is dependant on an intersticial network of inconsistently maintained spreadsheets. I&#39;d like to know if there&#39;s a single organization today that is not run this way.&lt;/p&gt;
&lt;p&gt;Spreadsheets are so good that even when Microsoft made their UI &lt;em&gt;hideous&lt;/em&gt; for a couple decades, they still took over the world.&lt;/p&gt;
&lt;br /&gt;
&lt;div class=&quot;content-full&quot;&gt;
  &lt;excel-ribbon-bloat&gt;&lt;/excel-ribbon-bloat&gt;
&lt;/div&gt;
&lt;p&gt;I&#39;m not aware of any programming environment since spreadsheets that have as fundamentally altered the cost of composing programs, and none that have enjoyed widespread adoption.&lt;/p&gt;
&lt;p&gt;It&#39;s worth taking a beat to appreciate what I just said. How is it &lt;em&gt;possible&lt;/em&gt; that the last major inflection point in computing&#39;s cost curve happened in 1979 on an Apple II?&lt;/p&gt;
&lt;p&gt;Of course you can&#39;t run web servers in spreadsheets. You can&#39;t build Sim City in spreadsheets. Spreadsheets aren&#39;t even turing complete without some scripting language. So while spreadsheets altered the cost of computing radically, on their own, they were limited to a certain category of program.&lt;/p&gt;
&lt;br /&gt;
&lt;div class=&quot;content-full&quot; id=&quot;spreadsheet-widget&quot;&gt;&lt;/div&gt;
&lt;p class=&quot;text-sm text-gray-600 italic&quot;&gt;
This visualization was created using &lt;a href=&quot;https://v0.dev/chat/spreadsheet-3-d-visualization-OUhSJmcYb4G?b=b_PIQK9pct9TP&quot; class=&quot;text-blue-600 hover:text-blue-800&quot;&gt;v0.dev&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What are the factors that contributed to the success of spreadsheets?&lt;/strong&gt; Here&#39;s my list.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Very low barrier to entry. They produced value for all users, regardless of technical background.&lt;/li&gt;
&lt;li&gt;Everything is a cell. &lt;em&gt;One abstraction&lt;/em&gt; to encapsulate both data and code. When you reference a cell, you don&#39;t care if it&#39;s a data cell or a code cell. It&#39;s a cell.&lt;/li&gt;
&lt;li&gt;Runtime === buildtime === prod. This thing is live! You didn&#39;t need to understand or debug some pipeline to get the thing to do something. Just open a file and you have the real thing.&lt;/li&gt;
&lt;li&gt;They were playful! Just edit a cell and everything reacts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s contrast this with &lt;a href=&quot;https://en.wikipedia.org/wiki/Smalltalk&quot;&gt;Smalltalk&lt;/a&gt;&lt;/p&gt;
&lt;img id=&quot;smalltalk-balloon&quot; class=&quot;invisible md:visible transform scale-75 z-40&quot; style=&quot;margin-bottom: -650px; margin-left: 650px;&quot; src=&quot;/img/cost-of-computing/Smalltalk_Balloon.svg&quot; /&gt;
&lt;p&gt;&lt;smalltalk-environment class=&quot;transform scale-50 translate-y-40 md:translate-y-0 my-24 md:scale-100 origin-left md:my-32&quot;&gt;&lt;/smalltalk-environment&gt;&lt;/p&gt;
&lt;p&gt;Smalltalk &lt;s&gt;was&lt;/s&gt; is awesome. Admittedly, as a 90s baby, I never really got to play with it in it&#39;s heyday. While Smalltalk never really broke out into mainstream society (ask the normies in your life about it, they&#39;ll blink at you), it&#39;d be crazy to call it a failure. It&#39;s influence on programming writ large cannot be understated.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Smalltalk was the first truly object-oriented programming environment.&lt;/li&gt;
&lt;li&gt;And the first instance (correct me if I&#39;m wrong) of a programming environment &lt;em&gt;running&lt;/em&gt; the program and giving the user feedback (like spreadsheets!).&lt;/li&gt;
&lt;li&gt;It was alive–no build step.&lt;/li&gt;
&lt;li&gt;Everything is an object. &lt;em&gt;One abstraction&lt;/em&gt; to encapsulate everything from data to code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The UI influenced modern operating systems and IDEs. In 1979, &lt;a href=&quot;https://spectrum.ieee.org/smalltalk&quot;&gt;Steve Jobs famously ripped off the Smalltalk UI&lt;/a&gt; for Lisa when he was shown it running on the Xerox Alto computer.&lt;/p&gt;
&lt;p&gt;While spreadsheets and Smalltalk were both reactive programming environments, Smalltalk just was just too expensive to be viable as an end-user programming environment. One of the project&#39;s stated goals was to be approachable to children. It wasn&#39;t. If it were, more children would have used it.&lt;/p&gt;
&lt;p&gt;In order to use Smalltalk, you had to write code. Code takes effort to read and write. It&#39;s fickle. Humans, for the most part, struggle to reason about it without experience. Unlike a spreadsheet, you can&#39;t just open up Smalltalk and fuck around and find out–at least not without a lot of effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Both&lt;/strong&gt; Smalltalk and Spreadsheets are governed by a single abstraction: For spreadsheets, the cell, and Smalltalk the object. One reason for the success of the spreadsheet is that it&#39;s single abstraction was &lt;em&gt;tangible&lt;/em&gt; to a novice user. The building block of the whole damn thing had a UI.&lt;/p&gt;
&lt;p&gt;Hypothesis: we can predict the success of a computing environment by judging the quality of the abstractions and the quality of their UX.&lt;/p&gt;
&lt;p&gt;Before LLMs, which I&#39;ll get to, the last time I got excited about a piece of technology maybe breaking the Computing Experience Curve was in 2013 or 2014 when I tried Airtable. Airtable took a powerful software pattern–the relational database–and put a &lt;em&gt;next level&lt;/em&gt; UI on top. High quality abstractions. High quality UX.&lt;/p&gt;
&lt;img alt=&quot;Airtable screenshot&quot; src=&quot;/img/cost-of-computing/airtable.webp&quot; class=&quot;content-wide-scaled&quot; /&gt;
&lt;p&gt;Airtable showed &lt;em&gt;so much&lt;/em&gt; promise. Everyone already used Google Sheets as a makeshift database - sharing lists where each row followed a consistent structure of columns. The familiarity of spreadsheets made it easy for anyone to contribute data and leverage formulas. But spreadsheets weren&#39;t built to be databases, even if you could awkwardly force them into that role. But, it was the best tool for the job because there was no one&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt; in the market making an end-user facing database that made any sense.&lt;/p&gt;
&lt;p&gt;The abstractions of a relational database (tables, rows, types, relations, constraints, etc) are more complex than the abstractions of spreadsheets. But, I thought that Airtable&#39;s UI was so good that maybe it could effectively teach novice users how it worked through experimentation. It seemed a hell of a lot easier than anything else out there.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;erDiagram&amp;#10;    ARTIST {&amp;#10;        int artist_id PK&amp;#10;        string name&amp;#10;        string nationality&amp;#10;        string birth_year&amp;#10;        string death_year&amp;#10;        text bio&amp;#10;    }&amp;#10;&amp;#10;    ARTWORK {&amp;#10;        int artwork_id PK&amp;#10;        int artist_id FK&amp;#10;        string title&amp;#10;        string year_created&amp;#10;        string medium&amp;#10;        string dimensions&amp;#10;        string image_url&amp;#10;        boolean on_display&amp;#10;    }&amp;#10;&amp;#10;    COLLECTION {&amp;#10;        int collection_id PK&amp;#10;        string name&amp;#10;        string description&amp;#10;        date creation_date&amp;#10;    }&amp;#10;&amp;#10;    GENRE {&amp;#10;        int genre_id PK&amp;#10;        string name&amp;#10;        string description&amp;#10;    }&amp;#10;&amp;#10;    ARTIST_GENRE {&amp;#10;        int artist_id FK&amp;#10;        int genre_id FK&amp;#10;    }&amp;#10;&amp;#10;    ARTWORK_COLLECTION {&amp;#10;        int artwork_id FK&amp;#10;        int collection_id FK&amp;#10;        date added_date&amp;#10;    }&amp;#10;&amp;#10;    ARTIST ||--o{ ARTWORK : creates&amp;#10;    ARTIST }|--o{ ARTIST_GENRE : belongs_to&amp;#10;    GENRE }|--o{ ARTIST_GENRE : has&amp;#10;    ARTWORK }o--o{ ARTWORK_COLLECTION : included_in&amp;#10;    COLLECTION }|--o{ ARTWORK_COLLECTION : contains&amp;#10;&lt;/pre&gt;
&lt;p&gt;Airtable took a leap of faith and trusted that the bigger market would take the time to learn the way Airtable worked. And in return, Airtable created a set of well designed, well thought out, inviting power tools. They had a generous free plan so people like me could try it (and then advocate to use it at work).&lt;/p&gt;
&lt;p&gt;And people like me loved it.&lt;/p&gt;
&lt;p&gt;I used Airtable to run my wedding&#39;s invite and RSVP system. I used Airtable to build &lt;a href=&quot;https://wewalkthevote.com/&quot;&gt;#walkthevote&lt;/a&gt; in 2020. For our wedding, we had tables like &lt;code&gt;households&lt;/code&gt;, &lt;code&gt;invites&lt;/code&gt;, &lt;code&gt;rsvps&lt;/code&gt; (which had dietary preferences), &lt;code&gt;gifts&lt;/code&gt; (for keeping track of thank yous). We had almost everyone&#39;s cell phone number, but we didn&#39;t have a lot of physical addresses. I wrote a web service to text everyone and have a dialog with them to gather their address and insert it into the address table.&lt;/p&gt;
&lt;img src=&quot;/img/cost-of-computing/airtable-circles.svg&quot; class=&quot;content-wide-scaled&quot; /&gt;
&lt;p&gt;Unfortunately for Airtable, Inc, while their UI is awesome, it&#39;s not awesome enough to make this concept understandable to a novice user before they give up. &lt;em&gt;Some&lt;/em&gt; users embraced the tool and put in the work to learn it. But that market just wasn&#39;t nearly as big as Airtable, Inc had hoped. To be clear, Airtable is a successful company, and if you ask them, they&#39;ll tell you that it&#39;s awesome that they&#39;ve sold into so many fortune 500 companies. But they are not on track to become a verb, change the cost curve of computing. They&#39;re &amp;quot;just&amp;quot; normal innovation.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;One afternoon in August, I remember nearly doing a spit take when trying out a new thing from Supabase.&lt;/p&gt;
&lt;div class=&quot;content-wide-scaled&quot;&gt;
    &lt;img class=&quot;lg:object-cover w-full transform -rotate-1&quot; src=&quot;/img/cost-of-computing/database.build.png&quot; /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;So we&#39;re finally getting to LLMs.&lt;/p&gt;
&lt;p&gt;☝️ &lt;a href=&quot;https://database.build&quot;&gt;This thing&lt;/a&gt; (called database.build but previously called postgres.new) is so fucking cool.&lt;/p&gt;
&lt;p&gt;It&#39;s exactly what Airtable was missing in their first-time UX.&lt;/p&gt;
&lt;p&gt;On some level it&#39;s &amp;quot;just&amp;quot; another browser-based agentic code generators not unlike &lt;a href=&quot;https://www.anthropic.com/news/artifacts&quot;&gt;Claude Artifacts&lt;/a&gt; or &lt;a href=&quot;https://openai.com/index/introducing-canvas/&quot;&gt;OpenAI Canvases&lt;/a&gt; or &lt;a href=&quot;https://v0.dev/&quot;&gt;V0&lt;/a&gt;. In all of these cases, a runnable artifact is produced and evaluated as you chat back and forth with the LLM. But with database.build, the code takes a back seat in the UI. Instead of reviewing code, you review a pretty sick diagram made with &lt;a href=&quot;https://reactflow.dev/&quot;&gt;react-flow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Database.build runs a &lt;em&gt;real&lt;/em&gt; postgres database &lt;em&gt;inside the browser&lt;/em&gt; with &lt;a href=&quot;https://pglite.dev/&quot;&gt;pglight&lt;/a&gt;. If a DDL statement produced by the LLM doesn&#39;t work in the real database, the LLM gets that feedback and tries again. It&#39;s typically able to self correct. If you had asked me in 2016 when this kind of technology would come to the market, I would have said &amp;quot;never.&amp;quot;&lt;/p&gt;
&lt;p&gt;Here&#39;s basically how database.build works:&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;flowchart LR&amp;#10;    User((User)) --&amp;gt;|&amp;quot;Natural Language Request&amp;quot;| LLM&amp;#10;&amp;#10;    LLM --&amp;gt; DDL[SQL DDL]&amp;#10;    DDL --&amp;gt;|&amp;quot;Generate visualization&amp;quot;| Diagram&amp;#10;    Diagram --&amp;gt;|&amp;quot;Reviews&amp;quot;| User&amp;#10;    subgraph WASM[WebAssembly Runtime]&amp;#10;        DB[(Postgres)]&amp;#10;    end&amp;#10;    DDL --&amp;gt;|&amp;quot;Execute&amp;quot;| DB&amp;#10;    DDL --&amp;gt; LLM&amp;#10;&amp;#10;&amp;#10;    linkStyle 0,1,4 stroke:#e74c3c,stroke-width:2px;&amp;#10;    linkStyle 2,3,5 stroke:#2ecc71,stroke-width:2px;&amp;#10;&lt;/pre&gt;
&lt;p&gt;There are two general categories of cost when developing software: &lt;strong&gt;reading code&lt;/strong&gt; and &lt;strong&gt;writing code&lt;/strong&gt;. Here You write in english and you read a diagram. When things get off the rails, you can open up the DDL and fix it.&lt;/p&gt;
&lt;p&gt;In the future, the user might not need to know about the DDL at all.&lt;/p&gt;
&lt;p&gt;I wouldn&#39;t be surprised if Supabase ends up being the company to build the first database that the normies &lt;em&gt;actually&lt;/em&gt; start using, although that doesn&#39;t seem to be their mission. Airtable tried to make creating a database easier by building fancy point-and-click UI. Supabase is making it easier by using an LLM (which, to be fair, wasn&#39;t available to Airtable when they started). Their core units of abstraction are the same, but their presentation and interaction layers are totally different.&lt;/p&gt;
&lt;p&gt;Hypothesis: One big reason this thing works as well as it does is because of it&#39;s constraints. There are only so many things that are possible in a relational database! Contrast this with V0/etc. The universe of possibilities is far far wider, and that makes it harder to get just right. It also limits what&#39;s possible with database.build. You can&#39;t build a 3d visualization in a database, after all.&lt;/p&gt;
&lt;p&gt;None of the projects I&#39;ve talked about thus far are &amp;quot;&lt;em&gt;real&lt;/em&gt;&amp;quot; programming environments. They&#39;re all toys. You can use them to build impressive stuff, but you don&#39;t maintain real Big Kid Software (todo: trademark &amp;quot;Big Kid  Software&amp;quot;). in them. Of course.&lt;/p&gt;
&lt;p&gt;That brings me to another pattern that&#39;s very hot right now: IDE + AI. Examples include &lt;a href=&quot;https://www.cursor.com&quot;&gt;Cursor&lt;/a&gt;, &lt;a href=&quot;https://zed.dev/&quot;&gt;Zed&lt;/a&gt;, &lt;a href=&quot;https://github.com/features/copilot&quot;&gt;Copilot&lt;/a&gt; and more.&lt;/p&gt;
&lt;p&gt;Cursor is an interesting case that I should discuss briefly. I use it. It&#39;s good. Quite good, even. It&#39;s kind of incredible that they&#39;ve continued to lap microsoft over and over again using their &lt;a href=&quot;https://github.com/microsoft/vscode&quot;&gt;very own open sourced IDE as a foundation&lt;/a&gt;. Copilot, Microsoft&#39;s version, started first but somehow seems to be constantly playing catch up. It&#39;s not bad, it&#39;s just not as good as Cursor.&lt;/p&gt;
&lt;div class=&quot;content-wide-scaled&quot;&gt;
    &lt;img src=&quot;/img/cost-of-computing/cursor.avif&quot; class=&quot;shadow&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;All these IDE + AI apps are examples of &lt;em&gt;AI veneer&lt;/em&gt;. Take a product that&#39;s already good, slap some AI on top. There&#39;s nothing wrong with veneer. Veneer is nice. You can improve a product with AI veneer. You can even improve it a lot! You can probably use AI veneer to reduce the price of programming by some meaningful amount. But. I promise you it is a trap headed to a plateau. These apps are going to make it possible for product managers to make really cool prototypes. They&#39;re not going to change the nature of software.&lt;/p&gt;
&lt;p&gt;It&#39;d be a tragedy if in the 2060s we were still coding in text files on little screens with keyboards and LLMs.&lt;/p&gt;
&lt;p&gt;We are having a mental cache invalidation event and we need to revisit our priors. Every programming environment idea we&#39;ve ever thought of that was &amp;quot;awesome but impossible&amp;quot; or &amp;quot;science fiction&amp;quot; deserves another look. Having fuzzy (even flawed), intelligent building blocks in our computers opens up the possibility for a truly different programmable medium.&lt;/p&gt;
&lt;p&gt;Of course there are people working on this. Lots of them. I know some. The folks at &lt;a href=&quot;https://tldraw.dev/&quot;&gt;TLDraw&lt;/a&gt; are doing some really interesting stuff. In theory they&#39;re a collaborative whiteboard company. And their flagship product is quite good. But they seem quite preoccupied with thinking about computing and computational media. In 2023 they made &lt;a href=&quot;https://tldraw.dev/blog/product/make-real-the-story-so-far&quot;&gt;MakeReal&lt;/a&gt;, a tool that let users draw a UI mockup and get a working prototype–sort of like V0 but without text prompting. Then recently they announced &lt;a href=&quot;https://computer.tldraw.com/&quot;&gt;TLDraw Computer&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;content-wide-scaled&quot;&gt;
  &lt;img src=&quot;/img/cost-of-computing/tl-computer.png&quot; alt=&quot;Computer&quot; class=&quot;rounded shadow border&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;I&#39;m not saying we&#39;re all going to be writing programs in TLDraw Computer in several years. I don&#39;t think TLDraw is saying that. I don&#39;t think you could write TLDraw in TLDraw Computer. But. They are doing the work. They&#39;re rethinking the foundational assumptions. And I&#39;m really eager to see what&#39;s next.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I think we very well might be in an inflection point in the Efficiency Curve. We probably are. The question is, how low will we go. If we stop at AI assisted &lt;em&gt;coding&lt;/em&gt;, and lose sight of the dream of AI assisted &lt;em&gt;programming&lt;/em&gt;, we&#39;re at risk of getting caught in a disappointment local minima. Instead, I&#39;d like to see a programming environment that&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a few high quality, powerful core abstractions&lt;/li&gt;
&lt;li&gt;very very good deeply intelligent UX on top of those core abstractions&lt;/li&gt;
&lt;li&gt;Is not a toy - you should be able to build modular units and hide complexity&lt;/li&gt;
&lt;li&gt;Is visual: end user sees and manipulates some visual representation of the program, not just code&lt;/li&gt;
&lt;li&gt;Exposes contracts and types as a first-order - we haven&#39;t really touched on this but it feels important&lt;/li&gt;
&lt;li&gt;Semantically searchable: &amp;quot;find where we make the api call to X&amp;quot; should be as easy as grepping&lt;/li&gt;
&lt;li&gt;Local first (a guy can wish right?)&lt;/li&gt;
&lt;li&gt;Fast (goes w/ the above)&lt;/li&gt;
&lt;li&gt;Fun&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;this post is still under construction&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;scrapbox&quot; tabindex=&quot;-1&quot;&gt;scrapbox &lt;a class=&quot;direct-link&quot; href=&quot;#scrapbox&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;img src=&quot;/img/cost-of-computing/abstraction-quality-vs-abstraction-ui-quality.svg&quot; class=&quot;content-wide-scaled&quot; /&gt;
&lt;style&gt;

    .comparison-container {
        width: 100%;
        overflow: hidden;
        max-height: 80vh;
    }
    .comparison-table {
        border-collapse: separate;
        border-spacing: 2px;
        font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, sans-serif;
        width: 100%;
        table-layout: fixed;
    }
    .comparison-table th {
        background-color: #fafafa;
        color: #555;
        padding: 12px 16px;
        text-align: left;
        font-weight: 500;
        font-size: 14px;
        border-bottom: 1px solid #eee;
        position: sticky;
        top: 0;
        z-index: 10;
    }
    .comparison-table th:first-child {
        z-index: 20; /* Higher z-index for corner cell */
        left: 0;
    }
    .comparison-table td {
        min-width: 120px;
        height: 30px;
        border-radius: 4px;
        position: relative;
    }
    .comparison-table td:first-child {
        background-color: #fafafa;
        color: #555;
        padding: 8px 16px;
        font-size: 14px;
        width: 16%;
        text-align: left;
        position: sticky;
        left: 0;
        z-index: 5;
    }
    /* Better colors */
    .comparison-table .good {
        background-color: #d4edda;
    }
    .comparison-table .ok {
        background-color: #fff3cd;
    }
    .comparison-table .bad {
        background-color: #f8d7da;
    }
    /* Plain cells for core abstraction row */
    .comparison-table .plain-cell {
        text-align: center;
        color: #555;
        font-size: 14px;
        padding: 8px;
    }
    /* Tooltip styling */
    .comparison-table td.has-tooltip {
        cursor: pointer;
        transition: all 0.2s ease;
    }
    .comparison-table td.has-tooltip:hover {
        transform: translateY(-2px);
        box-shadow: 0 2px 8px rgba(0,0,0,0.05);
    }
    .comparison-table td.has-tooltip::after {
        content: attr(data-content);
        visibility: hidden;
        width: max-content;
        max-width: 200px;
        background-color: #fff;
        color: #333;
        text-align: center;
        padding: 8px 12px;
        border-radius: 6px;
        position: absolute;
        z-index: 30;
        bottom: 125%;
        left: 50%;
        transform: translateX(-50%);
        opacity: 0;
        transition: opacity 0.3s, transform 0.3s;
        box-shadow: 0 3px 12px rgba(0,0,0,0.1);
        font-size: 13px;
        pointer-events: none;
    }
    .comparison-table td.has-tooltip:hover::after {
        visibility: visible;
        opacity: 1;
        transform: translateX(-50%) translateY(-5px);
    }
&lt;/style&gt;
&lt;div class=&quot;comparison-container&quot;&gt;
    &lt;table class=&quot;comparison-table&quot;&gt;
        &lt;thead&gt;
            &lt;tr class=&quot;header-row&quot;&gt;
                &lt;th&gt;&lt;/th&gt;
                &lt;th&gt;Spreadsheets&lt;/th&gt;
                &lt;th&gt;Smalltalk&lt;/th&gt;
                &lt;th&gt;Conventional Coding&lt;/th&gt;
                &lt;th&gt;Airtable&lt;/th&gt;
                &lt;th&gt;Database.build&lt;/th&gt;
                &lt;th&gt;TL Computer&lt;/th&gt;
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            &lt;tr&gt;
                &lt;td&gt;Core Abstraction&lt;/td&gt;
                &lt;td class=&quot;plain-cell&quot;&gt;Cell&lt;/td&gt;
                &lt;td class=&quot;plain-cell&quot;&gt;Object&lt;/td&gt;
                &lt;td class=&quot;plain-cell&quot;&gt;...&lt;/td&gt;
                &lt;td class=&quot;plain-cell&quot;&gt;Table/Relation&lt;/td&gt;
                &lt;td class=&quot;plain-cell&quot;&gt;Table/Relation&lt;/td&gt;
                &lt;td class=&quot;plain-cell&quot;&gt;Node&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Core Abstraction UI Quality&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Direct manipulation&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Object inspector&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Text files only&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Visual relationship builder&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Natural language to diagram&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Direct manipulation + LLM&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Instant Feedback&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Yes&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Yes&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;No&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Yes&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Yes&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Yes&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Entry Barrier&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Basic usage intuitive&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Requires programming&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Significant learning&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Some schema knowledge&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Natural language input&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Drawing + natural language&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Complexity Ceiling&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Limited logic complexity&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Full OOP paradigm&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Any computational model&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Limited to data operations&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Limited to data modeling&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Currently simple UI components&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Scale Ceiling&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;~1M cells practical limit&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Limited by memory&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Enterprise-scale&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;~100K rows practical&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Database size limitations&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Simple applications only&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;η Ratio (Effort/Thought)&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Direct formula entry&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Less syntax than most&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;High ceremony&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Point-click reduces effort&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Natural language interface&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;Draw + describe&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Domain Limitations&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Tabular calculations&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;General purpose&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;good has-tooltip&quot; data-content=&quot;General purpose&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;ok has-tooltip&quot; data-content=&quot;Data-oriented apps&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Database schemas only&quot;&gt;&lt;/td&gt;
                &lt;td class=&quot;bad has-tooltip&quot; data-content=&quot;Simple UI components&quot;&gt;&lt;/td&gt;
            &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;stuff-to-weave-in&quot; tabindex=&quot;-1&quot;&gt;stuff to weave in &lt;a class=&quot;direct-link&quot; href=&quot;#stuff-to-weave-in&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://river.maxbittker.com/&quot;&gt;https://river.maxbittker.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Making good software requires taste, craft, prioritization. Making software that changes the world requires new abstractions.&lt;/li&gt;
&lt;li&gt;Obsidian&lt;/li&gt;
&lt;li&gt;Dynamicland&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;airtable-evil&quot; tabindex=&quot;-1&quot;&gt;airtable evil? &lt;a class=&quot;direct-link&quot; href=&quot;#airtable-evil&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I can&#39;t talk this much about Airtable in a blog post without telling you that... they might be evil.&lt;/p&gt;
&lt;br /&gt;
&lt;div class=&quot;border rounded shadow transform -rotate-1&quot;&gt;
  &lt;img src=&quot;/img/cost-of-computing/airtable-limit.png&quot; /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;Recently, when I needed to access our wedding Base on Airtable, I was warned that since the last time I had used it, a new artificial row limit had been imposed of 1000 rows. That&#39;s insane. And stupid. If you&#39;re going to build a personal computing tool, and you&#39;re going to offer a free version to consumers, then you need to be able to plan a wedding in. I get charging companies for SAML integration, auditing and collaboration tools, support, etc. Makes perfect sense. Don&#39;t offer consumers a free plan if you can&#39;t plan a wedding in it. It forces last minute Saturday sqlite migration scripts. And that&#39;s annoying.&lt;/p&gt;
&lt;p&gt;And. If you&#39;re going to impose a stupid, pointless row limit, you gotta let our existing bases keep working.&lt;/p&gt;
&lt;p&gt;OK. That might be enough ripping into Airtable. They&#39;re still cool. But I&#39;m not using them anymore. If you&#39;re looking for recommendations, try &lt;a href=&quot;https://baserow.io/&quot;&gt;Baserow&lt;/a&gt; or &lt;a href=&quot;https://nocodb.com/&quot;&gt;Nocodb&lt;/a&gt;. Or, just use sqlite.&lt;/p&gt;
&lt;!--
Things we could talk about next




--&gt;
&lt;script src=&quot;/public/spreadsheet-widget/spreadsheet-widget.umd.cjs&quot;&gt;&lt;/script&gt;
&lt;script&gt;
document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; {
  const observer = new IntersectionObserver((entries) =&gt; {
    entries.forEach(entry =&gt; {
      if (entry.isIntersecting) {
        SpreadsheetWidget.mount(&#39;spreadsheet-widget&#39;);
        // Disconnect the observer after mounting to ensure it only happens once
        observer.disconnect();
      }
    });
  }, {
    // Adding a margin to start loading slightly before the element comes into view
    rootMargin: &#39;600px&#39;
  });

  const widgetElement = document.getElementById(&#39;spreadsheet-widget&#39;);
  if (widgetElement) {
    observer.observe(widgetElement);
  }
});
&lt;/script&gt;
&lt;style&gt;
body, html{
    overflow-x: hidden;
}
#spreadsheet-widget {
  height: 70vh;
}

/* Update your existing component styles */
visicalc-component {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

spreadsheet-cell-focus {
  width: 100%;
}
smalltalk-environment {
    height: 50vh;
    /* margin-top: 140px;
    margin-bottom: 70px; */
    /* transform: translate(-10px, 0) */
    /* overflow: hidden; */
}
&lt;/style&gt;
&lt;template id=&quot;visicalc-template&quot;&gt;
    &lt;style&gt;
      :host {
        display: block;
        font-family: monospace;
      }
      .visicalc {
        border: 2px solid #0f0;
        height: 100%;
        display: inline-block;
        max-width: 100%;
        overflow-x: auto;
        background-color: #000;
        color: #0f0;
      }
      .header {
        background-color: #0f0;
        color: #000;
        padding: 5px 10px;
        font-weight: bold;
        position: relative;
        width: 100%;
        box-sizing: border-box;
      }
      .visicalc {
        min-width: max-content;
      }
      .command-line {
        border-top: 2px solid #0f0;
        border-bottom: 2px solid #0f0;
        padding: 5px 10px;
      }
      .spreadsheet {
        display: table;
        border-collapse: collapse;
      }
      .row {
        display: table-row;
      }
      .cell {
        display: table-cell;
        border: 1px solid #030;
        padding: 2px 5px;
        min-width: 60px;
        max-width: 100px;
        overflow: hidden;
        white-space: nowrap;
      }
      .column-header {
        font-weight: bold;
        text-align: center;
        background-color: #020;
      }
      .row-header {
        font-weight: bold;
        background-color: #020;
      }
      .active-cell {
        background-color: #040;
      }
      .formula {
        color: #0ff;
      }
      .cell:empty::before {
        content: &quot;&gt;&quot;;
        color: #030;
      }
      /* Simulate phosphor burn-in effect on commonly viewed cells */
      .cell:nth-child(2) {
        text-shadow: 0 0 2px #0f0;
      }
      /* Add slight variation to the green color to simulate monitor wear */
      .visicalc {
        animation: screenFlicker 8s infinite;
      }
      @keyframes screenFlicker {
        0% { color: #0f0; }
        45% { color: #0f0; }
        46% { color: #0e0; }
        54% { color: #0f0; }
        100% { color: #0f0; }
      }
    &lt;/style&gt;
    &lt;div class=&quot;visicalc&quot;&gt;
      &lt;div class=&quot;header&quot;&gt;VisiCalc&lt;/div&gt;
      &lt;div class=&quot;command-line&quot;&gt;&amp;gt; D14&lt;/div&gt;
      &lt;div class=&quot;spreadsheet&quot;&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;
  &lt;script&gt;
    class VisiCalc extends HTMLElement {
      constructor() {
        super();
        const template = document.getElementById(&#39;visicalc-template&#39;);
        const shadowRoot = this.attachShadow({ mode: &#39;open&#39; });
        shadowRoot.appendChild(template.content.cloneNode(true));

        this.render();
      }

      generateColumnHeaders() {
        const row = document.createElement(&#39;div&#39;);
        row.className = &#39;row&#39;;
        row.innerHTML = `
          &lt;div class=&quot;cell column-header&quot;&gt;&amp;nbsp;&lt;/div&gt;
          ${Array.from(&#39;ABCDEFGHIJKLMNO&#39;).map(letter =&gt;
            `&lt;div class=&quot;cell column-header&quot;&gt;${letter}&lt;/div&gt;`
          ).join(&#39;&#39;)}
        `;
        return row;
      }

      generateDataRows() {
        const data = [
          [&#39;ACME Corp. Inventory&#39;, &#39;&#39;, &#39;&#39;, &#39;Date:&#39;, &#39;05/15/1979&#39;, &#39;&#39;, &#39;Rev. 2.1&#39;],
          Array(11).fill(&#39;&#39;),
          [&#39;Item&#39;, &#39;Quantity&#39;, &#39;Price&#39;, &#39;Total&#39;, &#39;&#39;, &#39;Sales Data&#39;, &#39;&#39;, &#39;&#39;, &#39;Notes&#39;],
          [&#39;Widget&#39;, &#39;150&#39;, &#39;9.99&#39;, &#39;@SUM(B4*C4)&#39;, &#39;&#39;, &#39;Month&#39;, &#39;Revenue&#39;, &#39;&#39;, &#39;Reorder @100&#39;],
          [&#39;Gadget&#39;, &#39;75&#39;, &#39;24.99&#39;, &#39;@SUM(B5*C5)&#39;, &#39;&#39;, &#39;Jan&#39;, &#39;5420&#39;, &#39;&#39;, &#39;Back-ordered&#39;],
          [&#39;Doohickey&#39;, &#39;200&#39;, &#39;14.50&#39;, &#39;@SUM(B6*C6)&#39;, &#39;&#39;, &#39;Feb&#39;, &#39;6100&#39;, &#39;&#39;, &#39;*Check pricing&#39;],
          [&#39;Gizmo&#39;, &#39;100&#39;, &#39;19.95&#39;, &#39;@SUM(B7*C7)&#39;, &#39;&#39;, &#39;Mar&#39;, &#39;7250&#39;, &#39;&#39;, &#39;New supplier&#39;],
          [&#39;Thingamajig&#39;, &#39;50&#39;, &#39;29.99&#39;, &#39;@SUM(B8*C8)&#39;, &#39;&#39;, &#39;Apr&#39;, &#39;8900&#39;],
          [&#39;Whatsit&#39;, &#39;120&#39;, &#39;12.75&#39;, &#39;@SUM(B9*C9)&#39;, &#39;&#39;, &#39;May&#39;, &#39;9150&#39;],
          [&#39;Doodad&#39;, &#39;85&#39;, &#39;8.50&#39;, &#39;@SUM(B10*C10)&#39;],
          [&#39;Contraption&#39;, &#39;35&#39;, &#39;45.00&#39;, &#39;@SUM(B11*C11)&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;, &#39;Discontinued&#39;],
          [&#39;Apparatus&#39;, &#39;95&#39;, &#39;16.25&#39;, &#39;@SUM(B12*C12)&#39;],
          [&#39;Gewgaw&#39;, &#39;160&#39;, &#39;7.99&#39;, &#39;@SUM(B13*C13)&#39;, &#39;&#39;, &#39;Proj. Revenue:&#39;, &#39;@SUM(G4:G9)/6*12&#39;],
          [&#39;&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;, &#39;Avg Monthly:&#39;, &#39;@AVG(G4:G9)&#39;],
          [&#39;Total Inventory Value:&#39;, &#39;&#39;, &#39;&#39;, &#39;@SUM(D4:D13)&#39;, &#39;&#39;, &#39;Growth Rate:&#39;, &#39;@PCT(G9,G4)&#39;]
        ];

        return data.map((rowData, i) =&gt; {
          const row = document.createElement(&#39;div&#39;);
          row.className = &#39;row&#39;;
          row.innerHTML = `
            &lt;div class=&quot;cell row-header&quot;&gt;${i + 1}&lt;/div&gt;
            ${rowData.map(cell =&gt;
              `&lt;div class=&quot;cell ${cell.startsWith(&#39;@&#39;) ? &#39;formula&#39; : &#39;&#39;}&quot;&gt;${cell || &#39;&amp;nbsp;&#39;}&lt;/div&gt;`
            ).join(&#39;&#39;)}
            ${Array(15 - rowData.length).fill(&#39;&lt;div class=&quot;cell&quot;&gt;&amp;nbsp;&lt;/div&gt;&#39;).join(&#39;&#39;)}
          `;
          return row;
        });
      }

      generateEmptyRows(start, end) {
        return Array.from({ length: end - start + 1 }, (_, i) =&gt; {
          const row = document.createElement(&#39;div&#39;);
          row.className = &#39;row&#39;;
          row.innerHTML = `
            &lt;div class=&quot;cell row-header&quot;&gt;${start + i}&lt;/div&gt;
            ${Array(11).fill(&#39;&lt;div class=&quot;cell&quot;&gt;&amp;nbsp;&lt;/div&gt;&#39;).join(&#39;&#39;)}
          `;
          return row;
        });
      }

      render() {
        const spreadsheet = this.shadowRoot.querySelector(&#39;.spreadsheet&#39;);
        spreadsheet.appendChild(this.generateColumnHeaders());

        this.generateDataRows().forEach(row =&gt; spreadsheet.appendChild(row));
        this.generateEmptyRows(11, 5).forEach(row =&gt; spreadsheet.appendChild(row));
      }
    }

    customElements.define(&#39;visicalc-component&#39;, VisiCalc);
  &lt;/script&gt;
&lt;script src=&quot;/public/small-talk.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/public/spreadsheet-cell.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/public/ms-ribbon.js&quot;&gt;&lt;/script&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Exponential_growth#Exponential_growth_bias&quot;&gt;Exponential Growth Bias&lt;/a&gt; is the formal term for the phenomenon that humans seem to understand and predict linear changes far easier than exponential ones. It&#39;s why people had a hard time accepting COVID at first. How could ~30 turn into millions so quickly?? &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;In 2001, $100 million was allocated to the Metro de Los Teques project in Venezuela to expand public transit and connect Los Teques with Caracas. Source: &lt;a href=&quot;https://www.caf.com/en/currently/news/caf-sets-record-in-2001-approving-operations-for-over-us-3100-million/&quot;&gt;CAF Development Bank Report&lt;/a&gt;. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;The Seattle Public Library renovated its Yesler branch in 2006 for $6.8 million as part of its &amp;quot;Libraries for All&amp;quot; bond issue. Source: &lt;a href=&quot;https://pcad.lib.washington.edu/building/5368/&quot;&gt;Seattle Public Library Renovation&lt;/a&gt;. &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Advanced traffic management systems cost between $500,000 and $4 million. For example, the I-90 Active Traffic Management system in Washington State cost around $12.9 million in 2010. Source: &lt;a href=&quot;https://app.leg.wa.gov/ReportsToTheLegislature/Home/GetPDF?fileName=ATM+Report+January+2013_4e6795ed-fd51-41f6-9e9a-9a674cfd61c5.pdf&quot;&gt;Washington State DOT Report&lt;/a&gt;. &lt;a href=&quot;#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Police patrol vehicles typically cost around $10,000 to $12,000 per unit in 2011. Source: &lt;a href=&quot;https://www.rrnm.gov/DocumentCenter/View/58928&quot;&gt;Rio Rancho Police Capital Improvement Plan&lt;/a&gt;. &lt;a href=&quot;#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Playground equipment costs vary but individual pieces can range from $3,000 to $5,000. Source: &lt;a href=&quot;https://www.playgroundequipment.com/&quot;&gt;Playground Equipment Cost Estimator&lt;/a&gt;. &lt;a href=&quot;#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Commercial-grade Automated External Defibrillators (AEDs) typically cost around $1,000 to $2,500 today. Source: &lt;a href=&quot;https://www.aedsuperstore.com/&quot;&gt;AED Superstore Pricing Guide&lt;/a&gt;. &lt;a href=&quot;#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;There were some other end-user-facing databases. One I remember messing around with as a kid was &lt;a href=&quot;https://en.wikipedia.org/wiki/Microsoft_Access&quot;&gt;Microsoft Access&lt;/a&gt;. I have a vague memory from the 90s of my mom working on a DOS database with my dad as they set up the infrastructure for her Greeting Card business. None of these technologies enjoyed widespread adoption, though. And it felt like Airtable really could. It had mobile support. It had tons of cool web tools. &lt;a href=&quot;#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>How to help a student get unstuck</title>
    <link href="/posts/how-to-help-a-student-get-unstuck/"/>
    <updated>2021-10-30T00:00:00Z</updated>
    <id>/posts/how-to-help-a-student-get-unstuck/</id>
    <content type="html">&lt;p&gt;When I was eight years old, I had a profound realization: once you know how to read, it’s impossible not to. If you see the letters c-h-a-i-r in sequence, your mind conjures sit-able 4-legged furniture. You don’t choose to read or not read, you just do it. And it’s impossible to understand what it’s like not to be able to read.&lt;/p&gt;
&lt;p&gt;The same principle applies when learning how to code. Once you understand how it works, it’s extremely challenging to imagine not understanding it. This can be problematic when teaching because it creates a gap between teacher and student. Even the most empathetic teachers can’t quite imagine themselves in a student’s shoes&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Getting a student from naivety to mastery takes a lot of work from the student and the teacher. But the work is asymmetrical. As a teacher, your role is to accompany, you can’t walk the student’s path for them. All you can do is support them through the journey as best you can.&lt;/p&gt;
&lt;p&gt;This article is intended to share some techniques I’ve developed for teaching debugging in a one-on-one setting. &lt;s&gt;If you find this useful, let me know, and I’ll write another one about helping students who are pair programming.&lt;/s&gt; I wrote &lt;a href=&quot;/posts/pairing&quot;&gt;a follow up article&lt;/a&gt; discussing dynamics in pair programming.&lt;/p&gt;
&lt;h2 id=&quot;forget-about-the-program-focus-on-the-programmer&quot; tabindex=&quot;-1&quot;&gt;Forget about the program, focus on the programmer &lt;a class=&quot;direct-link&quot; href=&quot;#forget-about-the-program-focus-on-the-programmer&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most programmers enjoy programming, and love solving a good problem. When you’re helping a student write a program, you have to fight the urge to fix the problem. Forget the program is even there. You may just want hop through a few files and figure out what’s going on. Stop. Fight that urge.&lt;/p&gt;
&lt;p&gt;While it can be marginally helpful for a student to see you at work, it’s not nearly as useful as it would be to help them solve the problem. You’re not there to get the code to work—that would be consulting. You’re there to get the &lt;em&gt;programmer&lt;/em&gt; to work.&lt;/p&gt;
&lt;div class=&quot;flex justify-center&quot;&gt;
  &lt;img class=&quot;sm:w-2/3&quot; src=&quot;/img/debugging/river.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;As programmers we construct an abstract mental model of the program we’re writing. Bugs are discrepancies between our mental model of the program and the program itself. The program is doing something we don’t &lt;em&gt;expect&lt;/em&gt;. The program isn’t &lt;em&gt;wrong&lt;/em&gt;. The program can’t be wrong. It’s doing what it’s been told to do. What’s incorrect is our mental model. Once we understand the current state of the program, we can adjust it.&lt;/p&gt;
&lt;div class=&quot;flex justify-center&quot;&gt;
  &lt;img src=&quot;/img/debugging/model.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;The art of debugging is systematically and creatively synchronizing the mental model with the actual program.&lt;/p&gt;
&lt;p&gt;As you teach, your focus should be on the student’s ability to keep the two in sync, not your own.&lt;/p&gt;
&lt;h2 id=&quot;don&#39;t-let-them-overheat&quot; tabindex=&quot;-1&quot;&gt;Don’t let them overheat &lt;a class=&quot;direct-link&quot; href=&quot;#don&#39;t-let-them-overheat&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Debugging can be frustrating. Every programmer knows the difference between productive friction and wheel-spinning. Your student can’t always tell the two apart. It’s your job to keep an eye on their frustration level, and adjust your support strategy accordingly.&lt;/p&gt;
&lt;p&gt;Consider both the frustration level of the student and the instructional value the moment offers.&lt;/p&gt;
&lt;p&gt;If they’re getting stuck on some tiny little problem, and the answer’s starring them right in the face, just fix it for them. It’s not worth pulling out the socratic method to fix a missing comma.&lt;/p&gt;
&lt;p&gt;If they’re profoundly frustrated, even if the moment offers tremendous instructional potential, hit pause. Take care of the human being in front of you. The programming concepts can come later.&lt;/p&gt;
&lt;div class=&quot;flex justify-center&quot;&gt;
  &lt;img src=&quot;/img/debugging/matrix.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;Depending on the level of trust you’ve built up with the student, you may be able to teach through more frustration than you would with a brand new student.&lt;/p&gt;
&lt;p&gt;So, You’ve identified a strategy. But how do you teach, how do you support and how do you debug?&lt;/p&gt;
&lt;h2 id=&quot;how-to-teach&quot; tabindex=&quot;-1&quot;&gt;How to teach &lt;a class=&quot;direct-link&quot; href=&quot;#how-to-teach&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Try to ask questions more often than you give information. It’s totally fine if you tell students things here and there, but try to lead them to answers whenever possible. I’ve found that sometimes I can deliberately slow down my own thought processes, and shield myself from what I think is the right answer. By doing this, I’m able to pose questions that I don’t know the answer to (“I wonder what the state of the user record is right now?”).&lt;/p&gt;
&lt;p&gt;If you’re teaching in-person, avoid touching a student’s keyboard. When I was new to teaching, I would sit on my hands so I wouldn’t be tempted to take over. If you need to show an example of something, use your own computer.&lt;/p&gt;
&lt;p&gt;Offer lots of encouragement. Every idea is a good idea. If they’re at risk of rabbit-holing, gently coax them out of it with a question, observation, or suggestion, for example, “that’s a great thought… I don’t think we’re going to find the answer there… tell me, do you think this code &lt;em&gt;ever&lt;/em&gt; runs? I’m starting to wonder if the problem is upstream.”&lt;/p&gt;
&lt;p&gt;When you get a sense of closure on a topic, prompt the student to summarize what they’ve learned. Finding the right time to summarize isn’t totally cut and dry, but I’ll offer two metaphors. When the “mental callstack” is empty, summarize. When get that feeling like “I should make a git commit,” summarize. Students sometimes get overwhelmed by the details, and if they never take a step back and appreciate the forest, all they’ll remember are trees.&lt;/p&gt;
&lt;h2 id=&quot;how-to-debug&quot; tabindex=&quot;-1&quot;&gt;How to debug &lt;a class=&quot;direct-link&quot; href=&quot;#how-to-debug&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So, you’ve decided that the best course of action is to take over and solve a student’s problem for them. The way you do it matters. You want to make sure you’re honoring the weight of the problem. They’re not stupid for not getting it. Show them that even professional programers get stuck, and that there’s a methodology for getting unstuck.&lt;/p&gt;
&lt;p&gt;It’s important that they can follow along. You might be making certain observations--voice them, and then check to see that they’re with you (“oh, it &lt;em&gt;can’t&lt;/em&gt; be a problem in the checkout service because we haven’t even passed validation, yet… do you agree?”). Make lots of diagrams.&lt;/p&gt;
&lt;p&gt;Practice good hygiene. Even if you might throw a bunch of random logs all over the place when you’re coding alone, try to demonstrate best practices. You don’t want to model your own bad habits.&lt;/p&gt;
&lt;p&gt;When it’s fixed, go over everything again. Talk about what the problem was, how we figured it out, and how we fixed it. Once you’ve done that, check and make sure they’ve got it.&lt;/p&gt;
&lt;h2 id=&quot;how-to-support&quot; tabindex=&quot;-1&quot;&gt;How to support &lt;a class=&quot;direct-link&quot; href=&quot;#how-to-support&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some moments are “teachable” and some aren’t. Learning to code is hard, and you will find students who are so frustrated or anxious that they can’t learn. This isn’t the time to pivot to your favorite debugging technique. Instead, be there for them. Hear their concerns and anxieties, and let them know that they’ll get through it.&lt;/p&gt;
&lt;p&gt;If you’re in a setting around other students, take them aside. It can be hard to have a vulnerable conversation in public. Maybe take a walk or go into another room.&lt;/p&gt;
&lt;p&gt;The most important thing you can do is listen. Ask them what’s bothering them. Make sure they know that you heard them by repeating it back in your own words. If you don’t have it right, keep working at it until you do.&lt;/p&gt;
&lt;p&gt;Lots of early stage programmers don’t realize that it’s &lt;em&gt;normal&lt;/em&gt; to struggle. They may not be aware that you had analogous struggles. While this isn’t about you—it’s about them—it can be helpful to tell them about the ways you struggled while you were learning, and the ways you struggle today.&lt;/p&gt;
&lt;p&gt;It can be hard for students to grasp the progress they’re making when they’re in a rut. At times, I’ve pulled up prior work from the student and emphasized the growth they’re making. “I know how frustrating this is. I just want you to know that you’re growing, and I see that. See this? You wrote this two months ago. I bet you’d do it totally differently today. What about this? You did this last week, you couldn’t do that two months ago.”&lt;/p&gt;
&lt;p&gt;Taking a break is almost always necessary before diving back into the IDE.&lt;/p&gt;
&lt;h2 id=&quot;putting-it-all-together&quot; tabindex=&quot;-1&quot;&gt;Putting it all together &lt;a class=&quot;direct-link&quot; href=&quot;#putting-it-all-together&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can mix and match these techniques. You can throw some out and add your own. It doesn’t have to be 100% one and 0% the others. You’re not a machine. Every teacher and every student is different. The art of teaching is finding a technique that works for the situation at hand.&lt;/p&gt;
&lt;p&gt;Can’t tell if it’s working? Ask your students. “When we took a walk and talked through things, was that helpful?” “When I ask you all these questions as you’re coding, do you find that useful? I sensed some frustration.” Just like you can’t know the state of an application without a breakpoint or log statement, you can’t know the state of your students without talking to them. What they say may surprise you, and it’s important for fine tuning your craft.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/user?id=Ozzie_osman&quot;&gt;@Ozzie_osman&lt;/a&gt; on &lt;a href=&quot;https://news.ycombinator.com/item?id=29051121&quot;&gt;hackernews&lt;/a&gt; told me that there&#39;s a formal term for this: &lt;a href=&quot;https://en.wikipedia.org/wiki/Curse_of_knowledge&quot;&gt;The Curse of Knowledge&lt;/a&gt; &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Zeke&#39;s new brand rollout</title>
    <link href="/posts/brand-identity/"/>
    <updated>2021-10-31T00:00:00Z</updated>
    <id>/posts/brand-identity/</id>
    <content type="html">&lt;p&gt;I&#39;m pleased to announce today that I&#39;m rolling out a new version of my personal brand, visual identity, and website. This is the culmination of a lot of work by my internal marketing function, and it&#39;s absolutely something to celebrate.&lt;/p&gt;
&lt;h2 id=&quot;new-headshot&quot; tabindex=&quot;-1&quot;&gt;New headshot &lt;a class=&quot;direct-link&quot; href=&quot;#new-headshot&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-3&quot;&gt;
  &lt;div class=&quot;border&quot;&gt;
    &lt;img src=&quot;/img/old-zeke-headshot.jpg&quot; style=&quot;margin:0&quot; /&gt;
    &lt;div class=&quot;p-2 text-sm text-gray-600&quot;&gt;Old headshot from 2019&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;border&quot;&gt;
    &lt;img src=&quot;/img/zeke.jpeg&quot; style=&quot;margin:0&quot; /&gt;
    &lt;div class=&quot;p-2 text-sm text-gray-600&quot;&gt;Today&#39;s new headshot from the inspired &lt;a target=&quot;_blank&quot; href=&quot;https://www.instagram.com/the_be_nice_comic/&quot;&gt;Be Nice Comic&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;new-logo-and-name&quot; tabindex=&quot;-1&quot;&gt;New logo and name &lt;a class=&quot;direct-link&quot; href=&quot;#new-logo-and-name&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-3&quot;&gt;
  &lt;div class=&quot;border flex flex-col&quot;&gt;
    &lt;div class=&quot;h-32 bg-gray-400 bg-gradient-to-b from-gray-200 to-gray-50 flex items-center justify-center flex-shrink-0&quot; style=&quot;margin:0&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;h-6 w-6&quot; fill=&quot;none&quot; viewBox=&quot;0 0 24 24&quot; stroke=&quot;currentColor&quot;&gt;
        &lt;path stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z&quot;&gt;&lt;/path&gt;
      &lt;/svg&gt;
    &lt;/div&gt;
    &lt;div class=&quot;p-2 text-sm text-gray-600&quot;&gt;There was no logo or brand name&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;border&quot;&gt;
    &lt;div class=&quot;h-32 bg-gray-400 bg-gradient-to-b from-gray-200 to-gray-50 flex items-center justify-center flex-shrink-0&quot; style=&quot;margin:0&quot;&gt;
      &lt;div style=&quot;transform: scale(0.9)&quot; class=&quot;
          shadow-2xl
          bg-gray-800
          ring-4 ring-offset-transparent ring-offset-2 ring-gray-800
          overflow-hidden
          flex flex-row
        &quot;&gt;
        &lt;div class=&quot;flex flex-col w-12&quot;&gt;
          &lt;div class=&quot;flex-grow&quot;&gt;&lt;/div&gt;
          &lt;div class=&quot;bg-gradient-to-bl h-2.5 bg-white&quot;&gt;&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;text-white whitespace-nowrap flex-grow text-5xl font-mono font-semibold p-4 pl-0&quot;&gt;
          off-by-one
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;p-2 text-sm text-gray-600&quot;&gt;Brand new name and logo&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Inspired by one of the most common programing errors, off-by-one evokes the sense that I&#39;m just a little bit out there... that a couple screws might be a little loose--a reality that I&#39;m embracing today.&lt;/p&gt;
&lt;p&gt;My internal design function burnt the midnight oil considering many alternate logos before finally landing on one that captured my ethos.&lt;/p&gt;
&lt;div class=&quot;flex justify-center&quot;&gt;
  &lt;img class=&quot;sm:w-2/3&quot; src=&quot;/img/alt-logos.png&quot; /&gt;
&lt;/div&gt;
&lt;h2 id=&quot;why-i-refreshed-my-brand-identity&quot; tabindex=&quot;-1&quot;&gt;Why I refreshed my brand identity &lt;a class=&quot;direct-link&quot; href=&quot;#why-i-refreshed-my-brand-identity&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My prior identity was established in early 2019. Today, we live in a different world. In 2019, I was attempting to appear professional, considered, and deliberate. But, the feedback from the community was that the brand was a touch boring. We heard you. With today&#39;s refresh, you&#39;ll find a more jovial, playful, and creative Zeke.&lt;/p&gt;
&lt;h2 id=&quot;engineering&quot; tabindex=&quot;-1&quot;&gt;Engineering &lt;a class=&quot;direct-link&quot; href=&quot;#engineering&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My internal engineering function has worked hard to deliver this site. It&#39;s build with &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; and &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt;. Most of my pre-existing content has already been ported.&lt;/p&gt;
&lt;h2 id=&quot;content&quot; tabindex=&quot;-1&quot;&gt;Content &lt;a class=&quot;direct-link&quot; href=&quot;#content&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can expect to read articles, luke-warm takes, talks and shitposts. The Content Function is still rerecording some older talks and articles. Stay on the lookout for those. They will be backdated to their original release dates.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>A word of caution on hiring rubrics</title>
    <link href="/posts/a-word-of-caution-on-hiring-rubrics/"/>
    <updated>2021-11-01T00:00:00Z</updated>
    <id>/posts/a-word-of-caution-on-hiring-rubrics/</id>
    <content type="html">&lt;p&gt;Engineers and businesspeople don’t always see eye to eye. One of the few things we truly have in common is an affinity for (or maybe an obsession with) the quantifiable. It’s no surprise, then, that many hiring processes are driven by rubrics. I’ve come to believe that—for all their benefits—hiring rubrics hurt organizations more than they help. This is especially true for small organizations.&lt;/p&gt;
&lt;p&gt;Hiring is scary. You have to make a yes/no decision about a &lt;em&gt;person&lt;/em&gt; with only squishy, intangible information to go off of. False positives are expensive and profoundly painful. False negatives are less painful but sometimes just as expensive because of the opportunity cost of losing someone truly great&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;In almost all cases, a group of people contribute to a hiring process. There are good and bad reasons for this. On the plus side, a successful candidate will have to work with a diverse group of people who serve in various roles. Getting a wide sampling of perspectives is undoubtedly a good idea: it increases the likelihood that someone will identify a true red flag or pick up on a crucial subtlety. Another reason many organizations put hiring decisions in the hands of committees is quite poor: no one wants to take the heat if a hire goes bad. It’s too scary for any individual to take the decision head-on, so the hot potato gets passed around the table.&lt;/p&gt;
&lt;p&gt;Sometimes committees find consensus; in many cases they don’t. This is where rubrics become important. Everyone fills out a survey about the interview, often with 1-5 as the options. Each candidate gets an aggregate score. This score can be compared to other candidates and may steer the committee towards a decision or even determine the outcome outright.&lt;/p&gt;
&lt;p&gt;Rubrics may make the process easier, but I’ve come to believe that they lead to worse outcomes&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. People feel better about rubric-driven processes because they mistake precision for accuracy&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. When someone sees a number, it feels like it comes from some kind of scientific instrument. But it doesn’t. At best, it’s a numerical representation of someone’s deliberate reflection, and at worst it’s a hot take.&lt;/p&gt;
&lt;p&gt;When rubrics are used at a company, someone, someday will be silently thinking, “Well, I really thought [candidate] would do great here, but I can’t argue with the fact that [candidate] scored a 6.2 on [topic]… I guess that’s not a good enough score.” All nuance is swept under the rug, everyone’s hands are clean, and no hard conversations are had. No one has to go out on a limb.&lt;/p&gt;
&lt;p&gt;Proponents of rubrics claim that they’re necessary for insuring consistency and fairness across a company. Without a rubric, they ask, how can you make sure you’re rooting out unconscious bias? At a certain scale, rubrics might be helpful in ensuring organizations operate consistently (although I think it’s pretty clear that this is often an illusion). But, I’m not so sure rubrics reduce bias—conscious or unconscious&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;. In fact, I think it might make the problem worse because it lets bias hide behind a number. The world is awash with examples of bias codified in rubrics or algorithms. For example, the SAT is &lt;a href=&quot;https://www.nytimes.com/2021/05/15/us/SAT-scores-uc-university-of-california.html&quot;&gt;widely understood to be discriminatory&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Proponents will ask how they can compare two candidates without a rubric. I would respond by asking them how often this is truly a problem they have. I’m sure there are some cases where companies run highly concurrent interview processes. But, in most cases, candidates aren&#39;t on the market at the same time. They come through as a stream and have a short time window in which you can make the hire. You might have to compare a candidate to 1 or 2 others, but the rest of the application pool has expired. Do you really need numerical data to make that kind of decision?&lt;/p&gt;
&lt;p&gt;So what do I propose instead? Well, honestly, this is hard stuff. If I had a perfect answer, I’d build a perfect company, get rich, and retire to build wind farms.&lt;/p&gt;
&lt;p&gt;I think the best answer I can come up with is one that embraces the hard part. Make someone go out on a limb. To be hired, you need someone to have &lt;em&gt;conviction&lt;/em&gt;. Anyone can raise a red flag.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have a committee interview the candidate. Make sure you have representation across various races, genders, roles, and experience levels. Consider excluding very recent hires who might not know the company dynamics well, yet.&lt;/li&gt;
&lt;li&gt;have interviewers fill out a short &lt;em&gt;qualitative&lt;/em&gt; survey. The survey should be a thinking tool. Make sure they fill it out before talking to other interviewers. You don’t want groupthink.&lt;/li&gt;
&lt;li&gt;Everyone meets and reviews the surveys (anonymously)&lt;/li&gt;
&lt;li&gt;If anyone raised any true &lt;em&gt;red flags&lt;/em&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt; in the surveys, the group discusses, but the candidate is rejected in all likelihood. The bar for dismissing a red flag is extremely high.&lt;/li&gt;
&lt;li&gt;The group discusses the candidate in an open fashion.&lt;/li&gt;
&lt;li&gt;Does anyone on the committee have conviction that this is a great hire&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;? If not, reject the candidate. It’s very important to create space for different styles of conviction to emerge. Not everyone communicates in the same way.&lt;/li&gt;
&lt;li&gt;If you really want to use numbers, try expressing conviction as a bet... &amp;quot;how much would you bet that [candidate] is able to succeed in this role.&amp;quot; That allows people to quantify confidence and risk sensitivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I like about this method is that it’s sensitive to disqualifying information, but also to positive conviction from an individual. All it takes to be rejected is a red flag. To be hired you need to have someone believe you’ll be amazing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have I tried this method with great success? No. This is what I’m going to try &lt;em&gt;next time&lt;/em&gt; I build a hiring process. So, take my advice with a grain of salt. My thoughts on this are fluid. And, even if I had used this method with great success, that doesn’t mean it would work for you. Hiring processes reflect culture, and every culture is different.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One final piece of advice—and this I write without any caveat—If you’re part of a hiring process that uses a rubric, and you don’t think it’s serving the company or the candidate well, you can do something about it. I’d suggest you voice conviction in spite of what the rubric says. Don’t sit quietly and let your conviction fade. That’s not to say you shouldn’t allow yourself to be influenced by your coworkers perspectives… you should. But don’t allow yourself to be silenced by a random number. You could say “I know the rubric has [candidate] as a 5.3 on [topic], but are we confusing precision with accuracy? I got a really good feeling about [candidate]’s ability to take on this role. I think we might have a misread on our hands.”&lt;/p&gt;
&lt;p&gt;I’d love to hear from you. Have you had experiences with rubrics—good or bad—that you think I’d benefit from hearing about? Let me know. Have you found a system that you believe in? I’d love to hear about that too.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;This is an example of &lt;a href=&quot;https://en.wikipedia.org/wiki/Loss_aversion&quot;&gt;Loss Aversion&lt;/a&gt; &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I don’t have any hard evidence for this, but based on my personal, subjective experience, I believe it &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;This is an example of &lt;a href=&quot;https://en.wikipedia.org/wiki/Precision_bias&quot;&gt;Precision Bias&lt;/a&gt; &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;It’s possible that rubrics could help if interviewers were blinded from identifying information about candidates, but this is in all likelihood impractical. Would that even be an interview? &lt;a href=&quot;#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;What’s a true red flag? That’s up to you to decide, but, in my opinion it’s discriminatory behavior, ethical concerns, incongruence with company values, etc. Red flags can sometimes be subtle, but they’re always about something serious, otherwise they wouldn’t be a red flag, they’d be a concern. &lt;a href=&quot;#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;It’s important that you don’t have trigger-happy interviewers who have conviction for everyone who walks in the door. I recommend that before someone starts conducting interviews, they shadow the process for a little while. They conduct interviews like normal, but their conviction isn’t enough to make the hire, at first. &lt;a href=&quot;#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Semantic operation layer</title>
    <link href="/posts/operations/"/>
    <updated>2021-11-02T00:00:00Z</updated>
    <id>/posts/operations/</id>
    <content type="html">&lt;p&gt;The last couple of applications I’ve developed have eventually ended up craving a design pattern that I’m starting to call an “operation layer.” I&#39;ve never fully realized it, but I think I&#39;m onto something here. As with so many things, this may already exist in some form. If so, please tell me about it.&lt;/p&gt;
&lt;h2 id=&quot;what&quot; tabindex=&quot;-1&quot;&gt;What &lt;a class=&quot;direct-link&quot; href=&quot;#what&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/operations/high-level.png&quot; alt=&quot;High level diagram&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Operations are units of action in your application. Your application doesn&#39;t do much of anything outside the context of an operation. Create a new user account? That&#39;s an operation. Send a reminder email? Another operation. Scheduled script to cleanup dangling data? That&#39;s an operation, too.&lt;/p&gt;
&lt;p&gt;Operations aren&#39;t just HTTP Requests. Jobs are operations. CLI commands are operations. Operations are a wrapper around a unit of work that could be connected to any transport layer.&lt;/p&gt;
&lt;p&gt;Operations are code, but they&#39;re also data. They exist in the codebase, but at runtime they produce artifacts that can be used to debug, analyse, and understand your users and application. They can be associated with a user, machine, etc.&lt;/p&gt;
&lt;p&gt;Operations can even be related to other operations. For example, if a user signs up for your service over HTTP with a &lt;code&gt;CreateAccount&lt;/code&gt; operation, and a &lt;code&gt;SendWelcomeEmail&lt;/code&gt; job operation is enqueued on a background process, the &lt;code&gt;SendWelcomeEmail&lt;/code&gt; operation has a &lt;code&gt;parentId&lt;/code&gt; referencing &lt;code&gt;SendWelcomeEmail&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/operations/detail.png&quot; alt=&quot;Detail level diagram&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;why&quot; tabindex=&quot;-1&quot;&gt;Why &lt;a class=&quot;direct-link&quot; href=&quot;#why&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Semantics&lt;/em&gt;. Operations are a semantic system that&#39;s decoupled from your API or transport layer. It&#39;s almost your &lt;em&gt;private&lt;/em&gt; public API.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Configuration&lt;/em&gt;. Operations can have all sorts of declarative metadata (whatever your application calls for... permissions, log levels, database transaction rules) that specify how the operation should work. Depending on your environment, they could be lined up 1:1 with input validation (in the ts world, maybe have 1 &lt;a href=&quot;https://github.com/typestack/class-validator&quot;&gt;class-validator&lt;/a&gt; class per operation?).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/img/operations/config.png&quot; alt=&quot;Configuration table&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Visibility&lt;/em&gt;. They can be tied into your logging, APM, and and error tracking systems, so that you can trace individual events back to a user and an operation. For example, they could produce structured logging artifacts that go into Elasticsearch through &lt;code&gt;stdout&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Adaptability&lt;/em&gt;. Today you might have a REST api in front of many operations, but you can switch that to a GraphQL API or something else down the road. By disconnecting your transport layer from your semantic layer, you aren&#39;t tied down to one technology. You could even go so far as to have Operations be a stepping stone to an event-driven architecture like Event Sourcing/CQRS, in which you persist operation data to a datastore, and have the rest of your application&#39;s state &lt;em&gt;react&lt;/em&gt; to that data in various projected datastores.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-would-this-look&quot; tabindex=&quot;-1&quot;&gt;How would this look? &lt;a class=&quot;direct-link&quot; href=&quot;#how-would-this-look&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;m going to demonstrate the way I imagine this working in a node app. This is fictional code.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; CreateAccountOperation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Operation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;createAccountOperation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CreateAccountInput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// the specific configuration options are arbitrary&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// and only intended to be illustrative&lt;/span&gt;&lt;br /&gt;  logLevel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LogLevels&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  allowUnauthenticated&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  timeout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  maxRetries&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function-variable function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; session &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createAccount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we were to expose this operation over HTTP, it would look something like this.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;createAccountController&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  CreateAccountOperation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; jwt &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;201&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; jwt &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; createAccountController&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I imagine &lt;code&gt;Operation&lt;/code&gt; would make use of node&#39;s new &lt;a href=&quot;https://nodejs.org/api/async_context.html#class-asynclocalstorage&quot;&gt;&lt;code&gt;AsyncLocalStorage&lt;/code&gt;&lt;/a&gt; class to make use of &lt;a href=&quot;https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope&quot;&gt;Dynamic Scoping&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; to share state to every future point in the callstack without passing it directly. In a way, it reminds me of React&#39;s &lt;code&gt;Context&lt;/code&gt;, if you&#39;re familiar with it.&lt;/p&gt;
&lt;p&gt;I&#39;m imagining something resembling the code sample below. This is pared down from my example for the sake of clarity, but it&#39;s basically right.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; operationStorage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AsyncLocalStorage&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  operationName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// perhaps userId?&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OperationConfig&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function-variable function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// here&#39;s where you put _your_ configuration items&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeOperation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OperationConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; operationStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        operationName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OperationConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OperationConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; runtimeOperation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeOperation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; runtimeOperation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;logging&quot; tabindex=&quot;-1&quot;&gt;Logging &lt;a class=&quot;direct-link&quot; href=&quot;#logging&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you wanted a logger that would always report the operation name and runtime ID, you could wrap your existing logging library like this:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bunyan &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bunyan&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; operationStorage &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./operation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bunyan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;log&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeWrappedLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;info&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;trace&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;debug&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fatal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;statement&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; operationName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; operationStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    logger&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;level&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;statement&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; operationName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; info &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeWrappedLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;info&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; trace &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeWrappedLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;trace&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; debug &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeWrappedLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;debug&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fatal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeWrappedLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fatal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If logs are being persisted somewhere searchable, this gives you a lot of power. If you know there&#39;s a problem with an operation, you can search for it by name. If you were to add a &lt;code&gt;userId&lt;/code&gt; to your &lt;code&gt;operationStorage&lt;/code&gt; you could also search by that.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;What do you think of this pattern? Tried anything similar? What were the results? Is there a framework or language that makes this easy?&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I learned about this term from my friend Omri Bernstein. Dynamic scope stands in contrast to lexical scope. To quote Omri, &amp;quot;Lexical scope is all about accessing named references based on code’s &lt;em&gt;definition&lt;/em&gt; whereas dynamic scope is all about accessing named references based on code’s &lt;em&gt;execution&lt;/em&gt;... React context and AsyncLocalStorage give you a &lt;em&gt;lexical&lt;/em&gt; reference that you can dereference to get a &lt;em&gt;dynamic&lt;/em&gt; reference, which prevents global clashing, and gives you back some of the traceability of lexical scope&amp;quot; &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Why is Excalidraw so fucking good?</title>
    <link href="/posts/why-is-excalidraw-so-good/"/>
    <updated>2021-11-04T00:00:00Z</updated>
    <id>/posts/why-is-excalidraw-so-good/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Disclaimer: This may read like an ad. It&#39;s not. No one&#39;s paying me to say this, I just love this product.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://excalidraw.com&quot;&gt;Excalidraw&lt;/a&gt;. Is. So. Good.&lt;/p&gt;
&lt;p&gt;Why? When you first open it, it looks kind of sloppy. It almost evokes &lt;a href=&quot;https://en.wikipedia.org/wiki/Kid_Pix&quot;&gt;Kidpix&lt;/a&gt;, or something. Let&#39;s dig into what makes it 👨‍🍳💋 from a product perspective.&lt;/p&gt;
&lt;p&gt;My threshold for trying new products is comically low. I&#39;ll try anything once. But, usually that&#39;s where it ends. I don&#39;t pride myself on being picky, it&#39;s just that most things don&#39;t find their way into my workflow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/excalidraw/distributions.png&quot; alt=&quot;Tool bar&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once I picked up Excalidraw, I haven&#39;t put it down. It became my tool-of-choice for diagrams, wireframes, brainstorms, and more. Part of why it was such a good fit for me is that it filled a massive hole in my toolkit. Before the pandemic, I would frequently sketch on whiteboards with my coworkers. In high school, I had a small classroom-sized whiteboard in my bedroom. The pandemic hit me like a ton of bricks in all sorts of ways. At work, one of the hardest parts was losing the white board. Of course, I could get a personal whiteboard, but then I wouldn&#39;t be able to really use it for &lt;em&gt;work&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So what is it about this software? Why does it work so well?&lt;/p&gt;
&lt;h2 id=&quot;you&#39;re-in&quot; tabindex=&quot;-1&quot;&gt;You&#39;re in &lt;a class=&quot;direct-link&quot; href=&quot;#you&#39;re-in&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&#39;s no onboarding. No signup. No confirmation email. No OAuth. You&#39;re just in the product. Are there any flashy overlay tours with annoying animations and pulsing buttons? Is there a modal upselling you on a paid plan&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;? No. You&#39;re just in product, able to use it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/excalidraw/landing.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;you-already-know-how-to-use-it.&quot; tabindex=&quot;-1&quot;&gt;You already know how to use it. &lt;a class=&quot;direct-link&quot; href=&quot;#you-already-know-how-to-use-it.&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s not overwhelming. There are 9 tools. The icons tell you what they do. There are no new concepts. If you don&#39;t know what something is, you can learn it in about 12 seconds.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/excalidraw/tools.png&quot; alt=&quot;Tool bar&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Other tools offer quite a bit more power, but require an investment to learn. I&#39;ve often wanted to try Omnigraffle after seeing what &lt;a href=&quot;https://twitter.com/g_lebec&quot;&gt;Gabe Lebec&lt;/a&gt; could make with it when we worked together. But, every time I&#39;ve tried it, I&#39;ve been overwhelmed by the learning curve (not to mention the dollar curve).&lt;/p&gt;
&lt;p&gt;I would resort to making diagrams in Keynote, HTML/CSS (just because I know it), Graphviz, or pen and paper.&lt;/p&gt;
&lt;h2 id=&quot;low-fidelity&quot; tabindex=&quot;-1&quot;&gt;Low fidelity &lt;a class=&quot;direct-link&quot; href=&quot;#low-fidelity&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/excalidraw/diagrams.png&quot; alt=&quot;Example diagram&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It forces you to stay low fidelity. Because everything is rendered with &lt;a href=&quot;https://roughjs.com/&quot;&gt;rough.js&lt;/a&gt; (I think...), objects look sloppy and hand-drawn. This liberates you from the details and lets you get to the point. In a proper design tool, an incomplete thought would feel discouraging. The product would beg for polish. In Excalidraw, polish is illegal, allowing you to stay in idea-space.&lt;/p&gt;
&lt;h2 id=&quot;intuitive-nod-to-pro-tools&quot; tabindex=&quot;-1&quot;&gt;Intuitive nod to pro tools &lt;a class=&quot;direct-link&quot; href=&quot;#intuitive-nod-to-pro-tools&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It respects standards. I&#39;ve spent a lot of time learning keyboard commands for other software (Keynote, InDesign, etc), and when I reflexively use them in Excalidraw... they work&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;! &lt;code&gt;Cmd + option + g&lt;/code&gt; creates a group, &lt;code&gt;Cmd + option + shift + g&lt;/code&gt; removes a group! You can hold down the space bar and drag the screen around. Too see what&#39;s possible, hit the question mark in the lower right corner.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/excalidraw/help.png&quot; alt=&quot;Help screenshot&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m personally not a dark-mode person&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;, but some people actually won&#39;t use an app unless it looks like the dark side of the moon. Here&#39;s a diagram I &lt;a href=&quot;/posts/operations&quot;&gt;recently&lt;/a&gt; made, rendered in dark mode.&lt;/p&gt;
&lt;img class=&quot;rounded-xl shadow&quot; src=&quot;/img/excalidraw/dark-mode.png&quot; /&gt;
&lt;h2 id=&quot;semantically-oriented&quot; tabindex=&quot;-1&quot;&gt;Semantically oriented &lt;a class=&quot;direct-link&quot; href=&quot;#semantically-oriented&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can build a vocabulary of objects. If you&#39;re using combinations of shapes to symbolize something, you can add them to your library of shapes, and recreate them when you need them. This is something I&#39;ve wanted in Keynote for a long time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/excalidraw/library.png&quot; alt=&quot;Library screenshot&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;collaborative-with-zero-friction&quot; tabindex=&quot;-1&quot;&gt;Collaborative with zero friction &lt;a class=&quot;direct-link&quot; href=&quot;#collaborative-with-zero-friction&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You don&#39;t have to be an Excalidraw evangelist to collaborate. Just send a link to your coworker. They don&#39;t have to sign up. They don&#39;t have to worry that a SaaS company will spam them for the rest of their life. They just click the link, and collaborate.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/excalidraw/collaboration.png&quot; alt=&quot;Collaboration screenshot&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-takeaway&quot; tabindex=&quot;-1&quot;&gt;The takeaway &lt;a class=&quot;direct-link&quot; href=&quot;#the-takeaway&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;re building a tool and you want your users to love you and to tell their friends:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reduce friction to zero&lt;/li&gt;
&lt;li&gt;Make. It. Simple.&lt;/li&gt;
&lt;li&gt;Don&#39;t upsell (or if you do, do it tastefully)&lt;/li&gt;
&lt;li&gt;Let users collaborate just as easily&lt;/li&gt;
&lt;li&gt;Build around users&#39; intuition&lt;/li&gt;
&lt;li&gt;Small building blocks can be pretty mighty.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;misc&quot; tabindex=&quot;-1&quot;&gt;Misc &lt;a class=&quot;direct-link&quot; href=&quot;#misc&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/_off_by_one/status/1362971962222534658&quot;&gt;https://twitter.com/_off_by_one/status/1362971962222534658&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/_off_by_one/status/1386405045684498432&quot;&gt;https://twitter.com/_off_by_one/status/1386405045684498432&lt;/a&gt;&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;They have recently taken a tiny piece of screen real estate to sell a paid plan, which is kind of annoying, have to say. But I guess they&#39;re a business? At least it&#39;s not too disruptive. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;10 years ago, one of the things I &lt;em&gt;loved&lt;/em&gt; about jQuery was how guessable the API was. I remember being able to work on a jQuery app on an airplane (when airplanes didn&#39;t have internet) because I didn&#39;t really have to look at the docs too much, I&#39;d just guess the function signatures. Sign of a great design. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I decided against adding a dark mode to this site, sorry dark-mode stans. &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Helping students with pair programming</title>
    <link href="/posts/pairing/"/>
    <updated>2021-11-05T00:00:00Z</updated>
    <id>/posts/pairing/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;I wrote an article about &lt;a href=&quot;/posts/how-to-help-a-student-get-unstuck/&quot;&gt;how to help a programming student get unstuck&lt;/a&gt;, in it, I offered to share my thoughts about how to help pairs of students get unstuck, and this is that article. If you haven&#39;t read the original, I&#39;d suggest starting there, because many of the techniques that work on individuals also work on students who are pair programming.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-pair-programming&quot; tabindex=&quot;-1&quot;&gt;What is pair programming &lt;a class=&quot;direct-link&quot; href=&quot;#what-is-pair-programming&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the purposes of this post, I&#39;m going to define pair programming somewhat narrowly. It&#39;s when two programmers are working on one computer, in collaboration, to write a program. When people pair, there are three versions of the program running--one on the computer, and two more in the minds of the programmers. If you&#39;ve ever worked with distributed systems, you know that adding nodes generally makes things more complex, and this is no exception.&lt;/p&gt;
&lt;img src=&quot;/img/pairing/pairing-high-level.png&quot; /&gt;
&lt;p&gt;When individuals are debugging code, they need to synchronize their mental model of the code with the actual program on the computer. When you pair, you have to sync your mental model with the program and the mental model of another human being. In order to do that, you have to do a lot of talking. I find pair programming to be profoundly rewarding, but also fatiguing for this reason.&lt;/p&gt;
&lt;h2 id=&quot;does-it-work-is-it-good-for-students&quot; tabindex=&quot;-1&quot;&gt;Does it work? Is it good for students? &lt;a class=&quot;direct-link&quot; href=&quot;#does-it-work-is-it-good-for-students&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pair programming may not be for everyone. Even if you like it, you may not love pairing with everyone. As in all human affairs, personalities and styles sometimes clash.&lt;/p&gt;
&lt;p&gt;By and large, code written by pairs is better than code written by an individual because it forces you to understand how your code will be read. Of course the code you just wrote makes sense to you--you just spent hours mind-melding with a computer. To make it run smoothly in someone else&#39;s brain, you have to also mind-meld with them.&lt;/p&gt;
&lt;p&gt;But what about pair programming for students, specifically? There&#39;s a wide range of experiences. The average student will learn more, and have a better experience pair-programming than writing code alone. They learn how to talk about their work--which is hard to teach, they find bugs themselves as they talk through their program, and they keep each other motivated when the going gets rough.&lt;/p&gt;
&lt;div class=&quot;flex flex-row justify-center&quot;&gt;
  &lt;img class=&quot;sm:w-2/3&quot; src=&quot;/img/pairing/quality-of-experience.png&quot; /&gt;
&lt;/div&gt;
&lt;h2 id=&quot;when-things-go-wrong&quot; tabindex=&quot;-1&quot;&gt;When things go wrong &lt;a class=&quot;direct-link&quot; href=&quot;#when-things-go-wrong&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When students are pair-programming, it can go sideways for countless reasons. Sometimes it&#39;s fixable, sometimes it&#39;s not. Here are &lt;em&gt;some&lt;/em&gt; of the causes of bad pairing experiences.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Confidence mismatch&lt;/strong&gt;. There&#39;s a surprisingly weak relationship between competence and confidence in early programmers (and beyond?). Sometimes a pair have similar understanding of the material, and similar capabilities, but one party will have far more conviction about their ideas. This can exacerbate imposter syndrome, and lead to other problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incompatible coping strategies&lt;/strong&gt;. Student A needs a break and student B needs to dig in. Student A wants to ask for help, student B isn&#39;t ready to give up. Student A wants to know that it&#39;s OK if they don&#39;t finish the exercise, and student B believes that thinking about failure brings it on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Misplaced authority&lt;/strong&gt;. As you observe them work, you notice that one person takes on the role of an authority figure. Sometimes, a student will pull up the wrong social template, and act with authority, greatly limiting the partnership. It&#39;s possible for this social template to come from either or both individuals. That is to say that sometimes someone is accustomed to working &lt;em&gt;for&lt;/em&gt; someone rather than &lt;em&gt;with&lt;/em&gt; them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communication crunch&lt;/strong&gt;. Take two normally good communicators, and then ask them to have a tactful conversation when they both really need to use the bathroom, and are changing lanes on the highway... they will no longer be good communicators. A similar phenomenon can happen with pair programming. You may find that some people unconsciously become tone deaf, inarticulate, and short tempered. This may or may not happen to everyone, and the severity varies. The effect tends to be proportional to mental strain, so as students become more comfortable with the material, this is less of a problem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bad vibes&lt;/strong&gt;. This is something of a catch-all. These two individuals just don&#39;t get along. They keep misreading each other&#39;s language.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How do you know if something isn&#39;t working? Often it&#39;s quite obvious, but it can be more subtle, at least early on. Here are some of the signs of a problem.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Not pairing&lt;/strong&gt;. Two computers, little dialog. This pair isn&#39;t pairing, they&#39;re collaborating on separate threads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signal imbalance&lt;/strong&gt;. You&#39;re called over to help, and you&#39;re only hearing from one person. The other person is checked out, or steamrolled, and the person doing the talking is ignoring their partner.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Command stream&lt;/strong&gt;. You overhear students working and their dialog sounds more like a stream of commands than a two-way collaboration. &amp;quot;now type new function... no parentheses here, I like to have arrow functions.&amp;quot; A healthy pair will generally be bouncing ideas, questions, and encouragement back and forth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frustration meltdown&lt;/strong&gt;. Sometimes things will get so bad that there&#39;s open hostility between students. This can happen for many reasons, but it&#39;s pretty hard to miss when it happens. It sounds like people fighting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These problems show up in classrooms, but they&#39;re not exclusive to them. I haven&#39;t been teaching in 5 years, and I see these kinds of problems all the time amongst coworkers. And just because I understand them, it doesn&#39;t mean that I&#39;m immune. Sometimes I even know what&#39;s happening but can&#39;t quite fix it.&lt;/p&gt;
&lt;h2 id=&quot;preventing-problems&quot; tabindex=&quot;-1&quot;&gt;Preventing problems &lt;a class=&quot;direct-link&quot; href=&quot;#preventing-problems&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&#39;s no way to prevent 100% of pairing problems. But, there are some things you can do from day 1 in a classroom to minimize them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Set expectations &amp;amp; norms&lt;/strong&gt;. A lot of frustration comes from mismanagement of expectations. Tell students to expect some friction. Tell them what is expected of them when it comes. Provide ground rules for how students are expected to treat each other.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Give them tips ahead of time&lt;/strong&gt;. &amp;quot;If you&#39;re having a hard time getting your point across, try a diagram. If that doesn&#39;t work, try to ask your partner to say it back in their own words.&amp;quot; &amp;quot;if you have a disagreement about direction...&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tell them why students do pair programming&lt;/strong&gt;. It&#39;s valuable, and there&#39;s a reason why you decided to set up your class in a certain way. They&#39;ll be more bought in once they understand that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adjust pairs&lt;/strong&gt;. Over the course of the term/semester/cohort, adjust who pairs with who. You&#39;ll get a sense of individual pairs chemistry and have ideas for better matches. Matching isn&#39;t all about skill level (I love pairing with programmers of almost all levels, learn a ton every time). Skill level can come into play, but matching on skill level alone will not solve your problems. You could get really data-driven about it, and I&#39;ve tried, but if your class-size is small enough, you&#39;ll learn who fits well with who. You can send our surveys, that&#39;s something I&#39;ve tried with mixed results. If you do that, make sure students know that their partner won&#39;t be in trouble if they give a negative rating on the experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-help&quot; tabindex=&quot;-1&quot;&gt;How to help &lt;a class=&quot;direct-link&quot; href=&quot;#how-to-help&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;flex flex-row justify-center&quot;&gt;
  &lt;img class=&quot;sm:w-2/3&quot; src=&quot;/img/pairing/stuck-together.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;When you are called to help a pair, fight the urge to sidestep interpersonal issues. If you observe a dysfunctional dynamic, help the relationship before helping either individual, and certainly before &amp;quot;helping&amp;quot; the computer. If you parachute into a toxic dynamic, tap a few keys, and make an error go away, you&#39;re kicking the can down the road&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. That&#39;s not to say you must completely fix the social problem before moving on--that is very likely impossible. But, if you ignore it, tensions will compound, and it will be harder to address later.&lt;/p&gt;
&lt;p&gt;Debugging interpersonal issues is tricky, and there are entire professions dedicated to it. There&#39;s no formula. If you expect yourself to be able to &amp;quot;fix&amp;quot; it, you&#39;re setting yourself up for disappointment. But, you can help, and over time things really can improve.&lt;/p&gt;
&lt;div class=&quot;flex flex-row justify-center&quot;&gt;
  &lt;div class=&quot;sm:w-2/3&quot; style=&quot;height:0;padding-bottom:56%;position:relative;&quot;&gt;&lt;iframe src=&quot;https://giphy.com/embed/lFeKAMNdm4DrVkRlUM&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A good starting point for solving any problem is understanding it. Build &lt;em&gt;your&lt;/em&gt; mental model of the &lt;em&gt;social&lt;/em&gt; problem. Ask the students what&#39;s going on. Name the tension, and see what happens. With no judgement in your voice, try something like, &amp;quot;I&#39;m picking up on some tension... is it fair to say you&#39;re both feeling some frustration right now? What&#39;s going on?&amp;quot; Then see what happens. If the students are willing to talk about how they feel, make sure to show them that you&#39;re hearing them. It&#39;s hard to predict where this goes, but sometimes it&#39;s cathartic for students to have the issue out in the open.&lt;/p&gt;
&lt;p&gt;Depending on the level of tension, you may to take a break before returning to content. &amp;quot;It seems like lunch is in 15 minutes, why don&#39;t we break early and come back to this first thing after lunch.&amp;quot; Time and nutrition can be as good a medicine as the best diagram or explanation.&lt;/p&gt;
&lt;p&gt;When everyone&#39;s ready to turn to content, you can act as a communication bridge to address many communication problems. Slow things down. Make sure at every step of the conversation, the two individuals hear each other. Point out for students when you think there&#39;s a communication problem. Try bringing in diagrams, rephrasing, examples. You can also inject a different energy into the dynamic with your own reactions, &amp;quot;ooh that&#39;s a really interesting question!&amp;quot; Just make sure it&#39;s authentic... injecting bullshit into the dynamic probably won&#39;t help. In a similar vein, when there&#39;s a win, take time to celebrate it.&lt;/p&gt;
&lt;p&gt;You can pull back the curtain and explain what you&#39;re doing. &amp;quot;I&#39;m trying to improve our communication dynamic because I think a lot of the problems we&#39;re having stem from the fact that we&#39;re not hearing each other.&amp;quot; You can also offer suggestions. &amp;quot;What if you used diagrams like we have over the last 15 minutes.&amp;quot;&lt;/p&gt;
&lt;p&gt;Throughout this process, you may experience strong feelings. You can get frustrated, too. Keep an eye on this. If you&#39;re getting frustrated, it&#39;s probably time for a break, a new approach, or to maybe pull in another teacher or TA. It is possible to make things worse.&lt;/p&gt;
&lt;p&gt;If nothing works, talk to them one-on-one later. See if they&#39;re more open when apart.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Pair programing is fraught with problems, but at the same time, it&#39;s 100% worth it in a classroom setting. Students learn more about code, more about their own communication dynamics, and are more resourceful than they would be alone. But, without some hands on social support, there will be enough failure cases that you might conclude it&#39;s not worth it.&lt;/p&gt;
&lt;p&gt;These techniques are personal. They may not work for every teacher or student, and that&#39;s OK. I hope they serve as fodder for thought and discussion. I&#39;d love to hear from other programming teachers about your experiences with pair programming in the classroom. Has it worked for you? Been a train-wreck? What do you do when students have problems?&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I&#39;m sure there are exceptions to this. Perhaps the error itself is adding to the tension, and taking it off the table right away will make it easier to figure out the real issue. Trust your gut. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Moving to a new job? You want a personal knowledge base</title>
    <link href="/posts/new-job-knowledge-base/"/>
    <updated>2021-11-13T00:00:00Z</updated>
    <id>/posts/new-job-knowledge-base/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Disclaimer: this is not an ad for Obsidian.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Onboarding to a new company is hard work. Keeping all the information straight &lt;em&gt;is its own work stream&lt;/em&gt; that deserves thought and attention. Countless names, faces&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, teams, initiatives, and acronyms fly by before noon. Believe me. I&#39;m in the process right now. This week I landed on a system I&#39;m pretty happy with.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/knowledge-base/graph.png&quot; alt=&quot;Obsidian graph&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;you&#39;re-a-knowledge-worker.-build-a-knowledge-base&quot; tabindex=&quot;-1&quot;&gt;You&#39;re a knowledge worker. Build a knowledge base &lt;a class=&quot;direct-link&quot; href=&quot;#you&#39;re-a-knowledge-worker.-build-a-knowledge-base&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You need a place to put all the information you&#39;re receiving. This is some of the stuff I&#39;m talking about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;People&lt;/li&gt;
&lt;li&gt;Teams&lt;/li&gt;
&lt;li&gt;Repositories&lt;/li&gt;
&lt;li&gt;Relationships between teams, repositories, people, etc&lt;/li&gt;
&lt;li&gt;Initiatives&lt;/li&gt;
&lt;li&gt;Your workproduct and tasks&lt;/li&gt;
&lt;li&gt;&amp;quot;Playbooks&amp;quot; for common tasks&lt;/li&gt;
&lt;li&gt;Daily agendas&lt;/li&gt;
&lt;li&gt;Meeting notes&lt;/li&gt;
&lt;li&gt;Ideas&lt;/li&gt;
&lt;li&gt;Questions you need to ask someone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I recommend &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt;. It&#39;s essentially a markdown editor--but it&#39;s so much more than that. It&#39;s open source. It works off of your local filesystem--no cloud-based privacy concerns. It encourages you to create a dense knowledge network with many links and tags. It also has a rich set of &lt;a href=&quot;https://help.obsidian.md/Plugins/Core+plugins&quot;&gt;official&lt;/a&gt; and &lt;a href=&quot;https://obsidian.md/plugins&quot;&gt;community plugins&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;plant-seeds&quot; tabindex=&quot;-1&quot;&gt;Plant seeds &lt;a class=&quot;direct-link&quot; href=&quot;#plant-seeds&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It can be quite intimidating to start from an empty directory. But, you don&#39;t have to build your knowledge base manually. Try seeding from available data sources.&lt;/p&gt;
&lt;h3 id=&quot;eng-wiki&quot; tabindex=&quot;-1&quot;&gt;Eng wiki &lt;a class=&quot;direct-link&quot; href=&quot;#eng-wiki&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Does your organization have an engineering wiki on github? Clone it and dump it right into your Obsidian folder. Now you have tons of pages instead of a blank slate.&lt;/p&gt;
&lt;p&gt;You are your company&#39;s foremost export on onboarding. No one else remembers what it was like to onboard, and--especially if they onboarded a long time ago--the experience could be quite different, now. You can use your vantage point to make the next hire&#39;s onboarding experience better. Contribute to the wiki as you&#39;re onboarding. Make it less confusing.&lt;/p&gt;
&lt;p&gt;I recommend checking out a branch of the repo so that any changes you make are PR-able. One really easy change that adds a ton of value is just adding hyperlinks. There&#39;s often an obscure page describing something that doesn&#39;t have any links going to it. Hyperlinks are a better discovery tool than grep, so add them.&lt;/p&gt;
&lt;h3 id=&quot;github-repos&quot; tabindex=&quot;-1&quot;&gt;Github repos &lt;a class=&quot;direct-link&quot; href=&quot;#github-repos&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add a markdown file in your knowledge base for every repo at your org. If you use Github, download the &lt;a href=&quot;https://cli.github.com/&quot;&gt;Github cli&lt;/a&gt;. From there you can generate a json file of the top &lt;code&gt;n&lt;/code&gt; &lt;a href=&quot;https://cli.github.com/manual/gh_repo_list&quot;&gt;repos&lt;/a&gt; at your org... if you can, maybe just get all of them. They are ordered by most recent commit.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ gh repo list &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;yourorgname&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--limit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt; name,description,createdAt,updatedAt`&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; your-repos.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wrote a little node script to generate markdown files&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; writeFileSync &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; repos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./your-repos.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pathToObsidian &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderMarkdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;# [&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;repo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;](https://github.com/yourorg/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;repo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&lt;br /&gt;	&lt;br /&gt;  &gt;&gt; whatever you want here, description is nice...&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; repo &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; repos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;	  &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;pathToObsidian&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/repos/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;repo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.md&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;br /&gt;	  &lt;span class=&quot;token function&quot;&gt;renderMarkdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;repo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;people-and-teams&quot; tabindex=&quot;-1&quot;&gt;People &amp;amp; Teams &lt;a class=&quot;direct-link&quot; href=&quot;#people-and-teams&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one is harder to make generic. At my new job, I found a pretty good list of the engineering org in Google Groups, copied the html table from the browser into Google sheets, then exported a CSV. Your source of truth might be Slack, Github Teams, or something totally different. You can also add people manually.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/knowledge-base/person.png&quot; alt=&quot;Create profiles for each person you meet with&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;version-tracking&quot; tabindex=&quot;-1&quot;&gt;Version tracking &lt;a class=&quot;direct-link&quot; href=&quot;#version-tracking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use git. Git init in your Obsidian directory. We use git for everything else, why not our knoledge base? Commit at the end of every day and take a look at the diff. Write a &lt;em&gt;real&lt;/em&gt; commit message. Don&#39;t worry about length. For me, it contains a better summary of my day than I can find anywhere else. &lt;code&gt;$ git log&lt;/code&gt; is now a pretty great way to see what you&#39;ve done in a week.&lt;/p&gt;
&lt;p&gt;Just make sure you have a &lt;code&gt;.gitignore&lt;/code&gt; file. In my case I ignore my &lt;code&gt;eng-wiki&lt;/code&gt; directory so I don&#39;t have to deal with git submodules.&lt;/p&gt;
&lt;h2 id=&quot;diagrams&quot; tabindex=&quot;-1&quot;&gt;Diagrams &lt;a class=&quot;direct-link&quot; href=&quot;#diagrams&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A picture is worth 1000 words.&lt;/p&gt;
&lt;h3 id=&quot;excalidraw&quot; tabindex=&quot;-1&quot;&gt;Excalidraw &lt;a class=&quot;direct-link&quot; href=&quot;#excalidraw&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There&#39;s an &lt;a href=&quot;https://github.com/zsviczian/obsidian-excalidraw-plugin&quot;&gt;Excalidraw plugin&lt;/a&gt; for Obsidian (thanks &lt;a href=&quot;https://twitter.com/zsviczian&quot;&gt;@zsviczian&lt;/a&gt;!). I&#39;ve already written about &lt;a href=&quot;https://offbyone.us/posts/why-is-excalidraw-so-good/&quot;&gt;how much I &lt;em&gt;love&lt;/em&gt; Excalidraw&lt;/a&gt;. The fact that Obsidian can elegantly integrate with Excalidraw is amazing. When I say elegantly, I mean it. You can link from inside diagrams to pages inside your knowledge base. You can embed. It&#39;s great.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/knowledge-base/excalidraw.png&quot; alt=&quot;Excalidraw&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;mermaid&quot; tabindex=&quot;-1&quot;&gt;Mermaid &lt;a class=&quot;direct-link&quot; href=&quot;#mermaid&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Excalidraw is a &lt;em&gt;great&lt;/em&gt; general purpose diagraming tool. But, for highly structured diagrams, &lt;a href=&quot;https://mermaid-js.github.io/mermaid/#/&quot;&gt;Mermaid&lt;/a&gt; is sometimes better. Obsidian has native support for Mermaid diagrams. Just make a code block annotated as &lt;code&gt;mermaid&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example from their docs.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;flowchart TD&amp;#10;  A[Start] --&amp;gt; B{Is it?};&amp;#10;  B -- Yes --&amp;gt; C[OK];&amp;#10;  C --&amp;gt; D[Rethink];&amp;#10;  D --&amp;gt; B;&amp;#10;  B -- No ----&amp;gt; E[End];&amp;#10;&amp;#10;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/img/knowledge-base/mermaid.png&quot; alt=&quot;Mermaid&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;questions&quot; tabindex=&quot;-1&quot;&gt;Questions &lt;a class=&quot;direct-link&quot; href=&quot;#questions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;1,000,000 questions will come up every day. There won&#39;t always be time to ask them. Some of them you can look up. I recommend making a system for these. I made a central questions page. When things come up on other pages, I link to my questions page. That way, when I want to review open questions, I can go look at all the backlinks. Tags also work.&lt;/p&gt;
&lt;p&gt;Every day take a look at open questions. Look through the company wiki, or other sources of truth. If you find the answer, update the link from &lt;code&gt;[[question]]&lt;/code&gt; to &lt;code&gt;[[answered-question]]&lt;/code&gt; and include the answer with any hyperlink to existing content in the knowledge base. For example, &lt;code&gt;[[answered-question]] what&#39;s the protocol for opening PRs into other teams&#39; repos? Answer: [[eng-wiki/code-review-process]] goes into this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For questions you can&#39;t answer for yourself, filter them to only those which matter and your team can answer, and slack them en mass, each day. Make sure the answers make their way back into your knowledge base.&lt;/p&gt;
&lt;h2 id=&quot;daily-notes&quot; tabindex=&quot;-1&quot;&gt;Daily notes &lt;a class=&quot;direct-link&quot; href=&quot;#daily-notes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/knowledge-base/dailynote.png&quot; alt=&quot;Daily note&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Obsidian has a &lt;a href=&quot;https://help.obsidian.md/Plugins/Daily+notes&quot;&gt;daily notes&lt;/a&gt; plugin that is very useful. First thing in the morning, I create a daily note, enter my google calendar agenda (hyperlinking to everyone I&#39;m meeting with). I always include a &lt;code&gt;meta&lt;/code&gt; section for observations about how the system is serving me. I also recommend creating links referencing the &lt;a href=&quot;https://forum.obsidian.md/t/fun-with-espanso/2317&quot;&gt;next and previous day&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I find that daily notes end up accumulating a ton of random stuff that came up that day. Take a little time to ask yourself if content on the daily note page should be relocated.&lt;/p&gt;
&lt;h2 id=&quot;tickets&quot; tabindex=&quot;-1&quot;&gt;Tickets &lt;a class=&quot;direct-link&quot; href=&quot;#tickets&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At least early on, every task you&#39;re assigned in your product management could also be tracked in your own personal note system. It allows you to link to relevant docs, take notes, draft status updates, gather questions, make diagrams, etc.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/knowledge-base/ticket.png&quot; alt=&quot;Ticket&quot; /&gt;&lt;/p&gt;
&lt;p&gt;My tickets directory isn&#39;t flat. Mine looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tickets
├── closed
│   ├── POP-696.md
│   └── POP-698.md
└── open
    └── POP-700.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Having the distinction between open and closed helps at standup when you want to tell your team how the ticket is going.&lt;/p&gt;
&lt;p&gt;Many of the tasks you complete when you&#39;re new on the job are somewhat rote. You&#39;re not re-architecting the home page in week one. For these repeatable tasks, is there good documentation about how to do it? Probably not. Make a playbook. It&#39;ll make it easier to do next time. If your playbook describes something beyond just you and your team, add it to the eng-wide wiki.&lt;/p&gt;
&lt;h2 id=&quot;retros-free-writing-and-synthesis&quot; tabindex=&quot;-1&quot;&gt;Retros, free writing, and synthesis &lt;a class=&quot;direct-link&quot; href=&quot;#retros-free-writing-and-synthesis&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If your team does retros, I recommend calendaring some time beforehand to do free writing. Even if your team doesn&#39;t have a retro tradition, scheduling some time for free reflection is a great idea--it encourages synthesis, connections, and understanding.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Think of it as part of your job description to minimize entropy.&lt;/em&gt; After you understand the ball of yarn that is your organization, you can untangle it, or at least reduce the rate at which it tangles. You can come up with analogies, diagrams, connections--anything that makes the system fit into brains better--you will add tremendous value. &lt;a href=&quot;https://distill.pub/2017/research-debt/&quot;&gt;Here&#39;s&lt;/a&gt; a great article about this general idea as it applies to researchers. They call it research debt. I think of it as &lt;em&gt;semantic debt&lt;/em&gt; or &lt;em&gt;cognitive debt&lt;/em&gt;. Reflective writing is a good way to prepare your brain for synthesis.&lt;/p&gt;
&lt;p&gt;When you write reflectively. Don&#39;t worry about formatting and linking while you free write, it could get in the way of flow. Once you&#39;re done, see if there are links to add or formatting to apply&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;It doesn&#39;t help that I have &lt;a href=&quot;https://en.wikipedia.org/wiki/Prosopagnosia&quot;&gt;prosopagnosia&lt;/a&gt;! &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>I returned my Remarkable2, here&#39;s why</title>
    <link href="/posts/remarkable/"/>
    <updated>2021-11-28T00:00:00Z</updated>
    <id>/posts/remarkable/</id>
    <content type="html">&lt;p&gt;I&#39;m going through one of those &lt;em&gt;moments&lt;/em&gt; in life where everything changes at once. As such, I&#39;ve been deliberately trying to branch out into new tools and new ways of thinking. A month-or-so ago, I bought a &lt;a href=&quot;https://remarkable.com/store/remarkable-2&quot;&gt;Remarkable&lt;/a&gt; tablet.&lt;/p&gt;
&lt;p&gt;At this point in the article, everyone who&#39;s known me for a long time is in shock. I have comically bad handwriting. So bad that it has a diagnosis--&lt;a href=&quot;https://en.wikipedia.org/wiki/Dysgraphia&quot;&gt;dysgraphia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I love typing notes. Most of my notes, and &lt;em&gt;all&lt;/em&gt; of my writing is digital. I refuse to write code by hand unless I&#39;m forced to in an interview. But some things are not easily typed: organic molecules, ERD diagrams, flow-charts, doodles... the list goes on. That&#39;s exactly why pen-and-paper has historically had a place in my toolkit despite its disadvantages.&lt;/p&gt;
&lt;p&gt;Paper also has a magical quality that digital tools lack. It captures the &amp;quot;air in the room.&amp;quot; When you review paper notes you can see how an agenda turns into an idea, turns into a meeting, turned into a doodle, turned into a diagram. You see how thoughts and events bleed together.&lt;/p&gt;
&lt;p&gt;One of the things that excited me greatly about getting a Remarkable tablet was that it might combine what I love about paper and what I love about computers. Like paper, I thought it would be easy to pick up and use. Like digital products, I it would let me stay organized and hyperlinked.&lt;/p&gt;
&lt;h2 id=&quot;i-went-for-it&quot; tabindex=&quot;-1&quot;&gt;I went for it &lt;a class=&quot;direct-link&quot; href=&quot;#i-went-for-it&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A few weeks before starting my new job, I decided now was the time to try Remarkable. It took a little while to arrive, but when it did, I was excited. The unboxing was a good experience. You can tell they spent a lot of energy on it (honestly probably too much considering its other deficits). It had an Apple feel, and even &lt;em&gt;smelled&lt;/em&gt; like a new mac.&lt;/p&gt;
&lt;p&gt;When I turned it on, I had one of my first disappointments. It was &lt;em&gt;really&lt;/em&gt; hard to to type my wifi password. I have a fairly long password, and it must have taken me 5 tries to get it right. This speaks to the fact that Remarkable didn&#39;t invest too much in the typing experience. That makes sense... after all, they are building an e-paper company. But this problem exemplified something deeper. &lt;em&gt;The non-writing features that you &lt;em&gt;need&lt;/em&gt; in order to use the product are flat-out bad.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Remarkable is harder than paper-and-pencil in some really crucial ways. You&#39;d think that it would make up for its weaknesses with digital benefits that paper can&#39;t provide. But it doesn&#39;t. &lt;em&gt;You&#39;d be better off with a paper notebook and a scanner.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;it&#39;s-too-small.-flipping-the-page-is-way-too-hard.&quot; tabindex=&quot;-1&quot;&gt;It&#39;s too small. Flipping the page is &lt;em&gt;way&lt;/em&gt; too hard. &lt;a class=&quot;direct-link&quot; href=&quot;#it&#39;s-too-small.-flipping-the-page-is-way-too-hard.&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/remarkable/page-transitions.png&quot; alt=&quot;hard page transitions&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When I finished onboarding, I decided to start a daily notes folder and make a notebook for each day. I filled up the first page quite quickly just drawing random stuff about the day. I was surprised how quickly the page filled up. It felt closer to writing on a large napkin than a piece of paper. I felt cramped. I looked it up, and Remarkable has the height and width of A5 paper.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/remarkable/adding-page.png&quot; alt=&quot;Hard to add a page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I should note that in theory there is a gesture I saw referenced in the onboarding materials to go to the &amp;quot;next&amp;quot; page, but I never could figure out how to make it happen, and I&#39;m not sure if it is supposed to add a page to the end of a notebook or just navigate between existing pages. When I wanted to &amp;quot;turn the page,&amp;quot; I was shocked at how hard it was. I had to give the sidebar menu a colonoscopy to find the UI to list all pages, then create a new one, then navigate to it. This gap in effort between my expectations and my experience was massive.&lt;/p&gt;
&lt;h2 id=&quot;the-eraser-is-kind-of-annoying&quot; tabindex=&quot;-1&quot;&gt;The eraser is kind of annoying &lt;a class=&quot;direct-link&quot; href=&quot;#the-eraser-is-kind-of-annoying&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I was relatively happy with the flexibility of the writing settings. You could write with different styles and widths. Writing felt good. Erasing was simple enough, but I did notice a subtle imprecision. The content you erased wasn&#39;t &lt;em&gt;quite&lt;/em&gt; where you put your eraser. It wasn&#39;t a deal breaker, but just annoying enough to note.&lt;/p&gt;
&lt;h2 id=&quot;shitty-file-system&quot; tabindex=&quot;-1&quot;&gt;Shitty file system &lt;a class=&quot;direct-link&quot; href=&quot;#shitty-file-system&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The filesystem they chose--folders and notebooks--are just not the best choice. Remarkable decided to use UI patterns from other tablets and computers but didn&#39;t mirror the UX quality that I&#39;ve come to expect. As I previously mentioned, their tablet-like UI is hard to use. It just takes too much effort to move things around and get organized.&lt;/p&gt;
&lt;h2 id=&quot;no-context-or-tie-ins&quot; tabindex=&quot;-1&quot;&gt;No context or tie-ins &lt;a class=&quot;direct-link&quot; href=&quot;#no-context-or-tie-ins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can&#39;t hook remarkable notes into anything. I wanted to use Remarkable in meetings. I figured there would be a way to automatically create notebooks for meetings and save that association to my calendar. That wasn&#39;t possible. As far as I could see there is no official API support either, so it&#39;s not possible to make a plugin.&lt;/p&gt;
&lt;p&gt;I wanted to have drop tags in notebooks--impossible.&lt;/p&gt;
&lt;p&gt;I figured that at least there&#39;d be some way to drop a breadcrumb on the page to reference something like the current time. Perhaps I could use that at the end of the day to cross-reference meetings with notes. This was also not possible.&lt;/p&gt;
&lt;h2 id=&quot;bad-sync&quot; tabindex=&quot;-1&quot;&gt;Bad sync &lt;a class=&quot;direct-link&quot; href=&quot;#bad-sync&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The sync feature between apps is just buggy. The setup is relatively slick, but notebooks get stuck in a stale state. I often needed to restart my Remarkable, my mac app, or both to get the latest notes to sync. There&#39;s no sync or refresh button.&lt;/p&gt;
&lt;h2 id=&quot;you-can&#39;t-really-read-books&quot; tabindex=&quot;-1&quot;&gt;You can&#39;t really read books &lt;a class=&quot;direct-link&quot; href=&quot;#you-can&#39;t-really-read-books&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remarkable is advertised as an E-Ink reader as well as a writing device. In reality, this only works if you have e-pub books on your hard drive. Where can you get e-pub books? Basically nowhere. There are many public-domain titles, but if someone were to purchase the Remarkable thinking they were also getting a kindle, they&#39;d be very disapointed. It&#39;s true that you can annotate PDFs on the remarkable. But, that wasn&#39;t something I had a particular itch to do. Perhaps if I was already being asked for feedback on PDFs in my regular workflow, that feature would have resonated with me differently.&lt;/p&gt;
&lt;h2 id=&quot;comically-bad-ocr-no-search&quot; tabindex=&quot;-1&quot;&gt;Comically bad OCR; No search &lt;a class=&quot;direct-link&quot; href=&quot;#comically-bad-ocr-no-search&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remarkable advertises that they can convert your handwritten notes into text. This doesn&#39;t really work. Firstly, it doesn&#39;t happen automatically, you have to do trigger the OCR yourself. When you trigger it, it&#39;s done per-page, not per notebook, which is kind of stupid. The text also isn&#39;t saved in any notebook hierarchy, it too is page-by-page.&lt;/p&gt;
&lt;div class=&quot;flex flex-row justify-center&quot;&gt;
  &lt;img class=&quot;sm:w-2/3&quot; src=&quot;/img/remarkable/ocr.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;The OCR quality is comically bad. It picks up a few words here and there, but it just isn&#39;t what you wrote. If it did work, there&#39;s no way to search all your notebooks for text and go to the right page.&lt;/p&gt;
&lt;h2 id=&quot;sum-it-up&quot; tabindex=&quot;-1&quot;&gt;Sum it up &lt;a class=&quot;direct-link&quot; href=&quot;#sum-it-up&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s take a moment to consider Remarkable vs pen, paper &amp;amp; scanner.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Remarkable&lt;/th&gt;
&lt;th&gt;Pen, paper &amp;amp; scanner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;&lt;s&gt;$399&lt;/s&gt; $299&lt;/td&gt;
&lt;td&gt;Free (phone) or ~$200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly subscription cost&lt;/td&gt;
&lt;td&gt;$5-8/m&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Battery life&lt;/td&gt;
&lt;td&gt;~14hrs&lt;/td&gt;
&lt;td&gt;∞&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shipping time&lt;/td&gt;
&lt;td&gt;Weeks&lt;/td&gt;
&lt;td&gt;Irrelevant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page flipping&lt;/td&gt;
&lt;td&gt;Annoying&lt;/td&gt;
&lt;td&gt;Trivial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;5-7/8 x 8-1/4 in&lt;/td&gt;
&lt;td&gt;8-1/2 x 11 in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Organizational scheme&lt;/td&gt;
&lt;td&gt;Use clunky UI to move things&lt;/td&gt;
&lt;td&gt;Not great. Either have binders or move scanned PDFs around&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eraser&lt;/td&gt;
&lt;td&gt;Bad&lt;/td&gt;
&lt;td&gt;Bad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;If you lose the pen&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://remarkable.com/store/remarkable-2/markers&quot;&gt;$50-$130!&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ask a friend? Buy a dozen at Staples?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I want to say that I don&#39;t intend to knock the Remarkable team. It&#39;s clear how much thought and energy and love they&#39;ve poured into their work. It just doesn&#39;t work or even make sense to me. What am I missing? Clearly something. I&#39;ve met multiple people who swear by this product. So it&#39;s doing something right! I&#39;d love to understand what makes Remarkable work when it works.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Work in progress: hookreducer.com</title>
    <link href="/posts/hook-reducer/"/>
    <updated>2021-12-19T00:00:00Z</updated>
    <id>/posts/hook-reducer/</id>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A dev tool that lets you setup an endpoint to accept webhooks &lt;strong&gt;super&lt;/strong&gt; easily. It&#39;s an order of magnitude easier to get up and running, and covers edge cases so you don&#39;t have to worry about it. Additionally, you can capture a little piece of state with a JS reducer function. Think Webhook + Redux (if that analogy makes any sense to you).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&#39;ve been a little quiet lately. In my hobby-time, I&#39;ve been working on a dev-tool micro-business. I think now is a good time to share it with the world! To be clear–this is half-baked stuff. I&#39;m sharing it because I believe that good things can happen when you work in public, and I&#39;m interested in seeing folks&#39; reactions. I also like leaving little time capsules of projects. I always wish I had done this more in the past. So if nothing else, this post will do that.&lt;/p&gt;
&lt;h2 id=&quot;the-idea:-hookreducer.com&quot; tabindex=&quot;-1&quot;&gt;The idea: &lt;a href=&quot;https://hookreducer.com&quot;&gt;hookreducer.com&lt;/a&gt; &lt;a class=&quot;direct-link&quot; href=&quot;#the-idea:-hookreducer.com&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/hr/logo.png&quot; alt=&quot;Hook Reducer Logo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(Logo courtesy of the &lt;a href=&quot;https://www.instagram.com/the_be_nice_comic/&quot;&gt;Be Nice Comic&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;There are a lot of places to run code on the internet. This product sets itself apart from Glitch, RunKit, CodeSandbox, etc not just because of what it can do, but also because of what it can&#39;t. You can&#39;t run any async code. You can&#39;t access the network. You can&#39;t import any npm packages. What you get instead are special webhook-specific features. It handles idempotency tokens, request authenticity verifications, queueing inbound requests, and more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/hr/screenshot.png&quot; alt=&quot;Hook Reducer Prototype Screenshot&quot; /&gt;&lt;/p&gt;
&lt;p&gt;What makes this super awesome is that when you change the reducer code, you can flow all your historical events through your reducer again to recreate the state. You can even set up an endpoint and leave the reducer function blank! Tell your webhook provider to send over data, and write your reducer code later!&lt;/p&gt;
&lt;h3 id=&quot;user-interface-elements&quot; tabindex=&quot;-1&quot;&gt;User Interface Elements &lt;a class=&quot;direct-link&quot; href=&quot;#user-interface-elements&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;resource-bar&quot; tabindex=&quot;-1&quot;&gt;Resource Bar &lt;a class=&quot;direct-link&quot; href=&quot;#resource-bar&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The resource bar is a top level component. It gives you at-a-glance access to important information about how your hook is running, but also how your hook sits relative to other hooks. Clicking on it will likely bring up some kind of modal/popover interface that gives you more access to some of these concepts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/hr/resource-bar.png&quot; alt=&quot;Resource Bar Screenshot&quot; /&gt;&lt;/p&gt;
&lt;h4 id=&quot;ide&quot; tabindex=&quot;-1&quot;&gt;IDE &lt;a class=&quot;direct-link&quot; href=&quot;#ide&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You write code that runs every time a request is received. You have the opportunity to define a set of functions to configure the hook&#39;s behavior, including &lt;code&gt;reducer&lt;/code&gt;, &lt;code&gt;getIdempotencyKey&lt;/code&gt; and &lt;code&gt;isAuthentic&lt;/code&gt;. There are several APIs available to you. The IDE has intellisense to help you explore your options, but you can also read (yet to be written) docs. Anything you &lt;code&gt;console.log&lt;/code&gt; will be visible in the Requests table.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/hr/ide.png&quot; alt=&quot;Code editor&quot; /&gt;&lt;/p&gt;
&lt;h4 id=&quot;requests-table&quot; tabindex=&quot;-1&quot;&gt;Requests Table &lt;a class=&quot;direct-link&quot; href=&quot;#requests-table&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This interface is inspired by the Network tab in Chrome Dev Tools. It lets you explore the requests your endpoint. There are a couple of notable features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You will be able to expand a request and see a diff of the state&lt;/li&gt;
&lt;li&gt;You can see an at-a-glance visual representation of the request body. I use &lt;a href=&quot;https://en.wikipedia.org/wiki/Locality-sensitive_hashing&quot;&gt;locality-sensitive hashing&lt;/a&gt; and &lt;a href=&quot;https://jdenticon.com/&quot;&gt;Jdenticon&lt;/a&gt; to make similar payloads appear similar.&lt;/li&gt;
&lt;li&gt;I want to build a custom-columns feature that allows you to surface parts of the request body or state, perhaps using a &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt; expression.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/img/hr/requests-table.png&quot; alt=&quot;Requests table&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;features&quot; tabindex=&quot;-1&quot;&gt;Features &lt;a class=&quot;direct-link&quot; href=&quot;#features&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Set up as many endpoints as you want and map them to reducer functions to retain state&lt;/li&gt;
&lt;li&gt;Queue up inbound traffic to handle spikes. No race conditions internally to HookReducer.&lt;/li&gt;
&lt;li&gt;Easily handle idempotency keys without writing any code&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Change&lt;/em&gt; your reducer function and recreate your state with the new code.&lt;/li&gt;
&lt;li&gt;Tag requests?&lt;/li&gt;
&lt;li&gt;Publish state
&lt;ul&gt;
&lt;li&gt;on a websocket&lt;/li&gt;
&lt;li&gt;to SQS?&lt;/li&gt;
&lt;li&gt;to another webhook address?
&lt;ul&gt;
&lt;li&gt;Maybe have the option to batch or debounce to that address? Have HookReducer handle the traffic spikes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;to Slack?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Leave at any time: you can export your code to a zip folder that will give you everything you need to run your endpoint on your own. You can also export all your event data&lt;/li&gt;
&lt;li&gt;Create multiple, revokable read and write keys&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;Write keys (endpoints) can map to multiple reducers?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pricing-and-accounts&quot; tabindex=&quot;-1&quot;&gt;Pricing &amp;amp; accounts &lt;a class=&quot;direct-link&quot; href=&quot;#pricing-and-accounts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You can create an endpoint for free without even making an account
&lt;ul&gt;
&lt;li&gt;If you don&#39;t create an account, your data end endpoint will be deleted after some period of time (2h? 2d?)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;With a free account, you can do a lot. Not sure exactly which features I will gate, but I&#39;m looking at making the free tier actually useful, not just a taste of the real experience&lt;/li&gt;
&lt;li&gt;Paid accounts ($5-$50/month... wide range, I know, I&#39;ll figure it out later) will offer higher execution limits, more features, isolated runtimes, support, etc. The idea is that if this has really made it into your workflow, and you&#39;re seeing lots of traffic, you need to pay for it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;this-solves-a-non-trivial-problem&quot; tabindex=&quot;-1&quot;&gt;This solves a non-trivial problem &lt;a class=&quot;direct-link&quot; href=&quot;#this-solves-a-non-trivial-problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Webhooks are ubiquitous. Server-server notifications come up any time you&#39;re building an internet product. For example, if you&#39;re building a SaaS product, you might need a provider like Stripe to send you a webhook any time someone&#39;s card is declined because you need to send them an email.&lt;/p&gt;
&lt;p&gt;Building an endpoint designed to accept webhooks isn&#39;t rocket science. But, there are some gotchas, and doing it well is non-trivial.&lt;/p&gt;
&lt;h3 id=&quot;idempotency&quot; tabindex=&quot;-1&quot;&gt;Idempotency &lt;a class=&quot;direct-link&quot; href=&quot;#idempotency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can&#39;t count on a webhook provider to send an event once and only once. If your system receives the same event twice, you might send the customer an email twice.&lt;/p&gt;
&lt;h3 id=&quot;spikes-in-requests&quot; tabindex=&quot;-1&quot;&gt;Spikes in requests &lt;a class=&quot;direct-link&quot; href=&quot;#spikes-in-requests&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sometimes you get unpredictable spikes in traffic from webhook providers. This can be for a variety of reasons&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. It&#39;s important that when you accept webhooks, you queue incoming requests to be processed asynchronously. That way your webhook doesn&#39;t take down production. HookReducer will be built to accept requests at scale and process them later. Everyone goes down, but the architecture is built to minimize downtime of endpoints.&lt;/p&gt;
&lt;h3 id=&quot;authenticity&quot; tabindex=&quot;-1&quot;&gt;Authenticity &lt;a class=&quot;direct-link&quot; href=&quot;#authenticity&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;How do you know if the request your endpoint is receiving is authentic? Anyone who knows the address of your endpoint could hit it. This is a kind of &lt;a href=&quot;https://en.wikipedia.org/wiki/Man-in-the-middle_attack&quot;&gt;&amp;quot;monster-in-the-middle attack&amp;quot;&lt;/a&gt;. Now, you could share a secret with your provider that only they know. But that isn&#39;t too different from an obscure URL. The way some services (e.g. Stripe, but many others) handle this is with an authenticity token. The token is often an &lt;a href=&quot;https://en.wikipedia.org/wiki/HMAC&quot;&gt;HMAC hash&lt;/a&gt; of the payload. If you know a shared secret that the provider shares with you, you can use it to hash the payload and compare it to the authenticity token sent. This isn&#39;t &lt;em&gt;the&lt;/em&gt; way to authenticate webhooks, it&#39;s &lt;em&gt;a&lt;/em&gt; way. But, the point is that Hook Reducer will give you the tools you need to authenticate hooks from a variety of sources.&lt;/p&gt;
&lt;h2 id=&quot;my-goals&quot; tabindex=&quot;-1&quot;&gt;My goals &lt;a class=&quot;direct-link&quot; href=&quot;#my-goals&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My primary goal is to make something people care about. For this to work, it has to pay for it&#39;s own hard costs, and if I can make a little money on top for my time, that&#39;s great. This is explicitly not a &amp;quot;startup.&amp;quot; I don&#39;t believe I&#39;ll ever raise money, hire employees, launch a marketing campaign. Once feature-complete, I&#39;d like to be able to run this thing with about 4hrs/week (with the exception of new product development). I work fulltime and this isn&#39;t intended to replace that.&lt;/p&gt;
&lt;p&gt;I also have &lt;a href=&quot;https://twitter.com/_off_by_one/status/1398056511155310594&quot;&gt;a chronic tendency&lt;/a&gt; to abandon side projects. My hope is that by working in public I&#39;ll be able to see this thing to it&#39;s natural conclusion!&lt;/p&gt;
&lt;h2 id=&quot;what-i&#39;m-looking-for-from-you&quot; tabindex=&quot;-1&quot;&gt;What I&#39;m looking for from you &lt;a class=&quot;direct-link&quot; href=&quot;#what-i&#39;m-looking-for-from-you&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I want to know if people are excited about this? Do I get old friends coming out of the woodwork to tell me they&#39;d use it? People telling me about feature ideas? Crickets? I&#39;m trying to take the pulse of the internet and see how you all feel. Have some advice for me? It is hereby solicited.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;If you want to remove someone&#39;s ability to see the state or change it, for example &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Examples: their queue got messed up after some downtime and they send you 5 hours of activity all at once, someone bulk updates customer records and they send you a request for each one, etc, etc &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Micro-business update: name change, software coming together</title>
    <link href="/posts/micro-business-update/"/>
    <updated>2021-12-25T00:00:00Z</updated>
    <id>/posts/micro-business-update/</id>
    <content type="html">&lt;style&gt;
  .mermaid {
    /* position: absolute; */
    margin: 6em 0!important;
    top: 10%;
    left: 10%;
    background: rgba(0,0,0,0.02)!important;
    border: 2px solid #ccc;
    transform: scale(1.2);
    /* border: 1px solid blue; */
    }
&lt;/style&gt;
&lt;p&gt;Hello! It&#39;s been about a week since I first wrote about working on &lt;a href=&quot;/posts/hook-reducer&quot;&gt;&lt;s&gt;Hook&lt;/s&gt;Web Reducer&lt;/a&gt;. I&#39;m on vacation from work and have some time to invest in this, and a lot has changed. I&#39;m getting closer and closer to shipping an MVP. That&#39;s the next milestone. The vision is also sharpening.&lt;/p&gt;
&lt;h2 id=&quot;business-updates&quot; tabindex=&quot;-1&quot;&gt;Business updates &lt;a class=&quot;direct-link&quot; href=&quot;#business-updates&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;name-change&quot; tabindex=&quot;-1&quot;&gt;Name change &lt;a class=&quot;direct-link&quot; href=&quot;#name-change&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Something about HookReducer wasn&#39;t sitting quite right with me. I &lt;em&gt;loved&lt;/em&gt; that I got the .com, and what I&#39;m building is well suited to webhooks. But it&#39;s not &lt;em&gt;just&lt;/em&gt; for webhooks. It&#39;s really for any kind of message. Initially, I&#39;m only accepting messages via HTTP (and it could stay that way forever). But the client doesn&#39;t &lt;em&gt;have&lt;/em&gt; to be a webhook provider.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://webreducer.dev&quot;&gt;&lt;strong&gt;WebReducer.dev&lt;/strong&gt;&lt;/a&gt; struck me as a better name. It&#39;s still easy to spell and say. I think it&#39;s memorable. The .dev TLD is generally less desirable than .com, but in this case, it immediately tells you that the product is a service for software development. &lt;a href=&quot;http://HookReducer.com&quot;&gt;HookReducer.com&lt;/a&gt; could be selling something to hang curtains, a sewing tool, etc, etc.&lt;/p&gt;
&lt;h3 id=&quot;open-source&quot; tabindex=&quot;-1&quot;&gt;Open source &lt;a class=&quot;direct-link&quot; href=&quot;#open-source&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&#39;ve decided to open source this project now, even though it&#39;s nowhere near feature-complete. I always intended to build an open product. I imagined features that would allow users to leave anytime they want, with their data and their code, and run on their own infrastructure. I was also leaning towards open sourcing everything. Once I made this decision to do it, I wondered when was the right time. I was a little bit fearful that someone would look at my code and judge me. Why did I use &lt;code&gt;x-library&lt;/code&gt;? Why don&#39;t I use Y Design pattern there?&lt;/p&gt;
&lt;p&gt;I&#39;ve reflected a little bit and gotten over these fears&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Criticism can be very useful, so why not have it&lt;/li&gt;
&lt;li&gt;When it isn&#39;t useful, ignore it&lt;/li&gt;
&lt;li&gt;If I let these fears prevent me from open sourcing, I won&#39;t really be building in public, which was part of the whole point of this project. I believe building in public is partly about letting people see the process, not just a polished product.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, I&#39;ve made &lt;a href=&quot;https://github.com/zekenie/web-reducer&quot;&gt;the repo&lt;/a&gt; public as of now.&lt;/p&gt;
&lt;h2 id=&quot;product&quot; tabindex=&quot;-1&quot;&gt;Product &lt;a class=&quot;direct-link&quot; href=&quot;#product&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve made some progress on value prop. I think I can articulate some use cases more clearly than I could last week. A point of emphasis: &lt;em&gt;this is a µ-product&lt;/em&gt;. It&#39;s designed for µ-problems, of which there are many. Here are some I&#39;ve thought of.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prototyping
&lt;ul&gt;
&lt;li&gt;Often times when you&#39;re working on a new project, it&#39;s somewhere between prototype and production application. Maybe you don&#39;t have as many tests as you normally would. Maybe you don&#39;t have as much observability as you might want. &lt;em&gt;Maybe you don&#39;t handle webhooks all that well&lt;/em&gt;. Start with WebReducer. Put it in front of your project.&lt;/li&gt;
&lt;li&gt;Maybe you have a new integration in your product. It&#39;s immature, and you&#39;re not sure if it&#39;ll stick around. They have a webhook feature, but you don&#39;t want to do the legwork to setup a queue, validate authenticity, etc, etc, etc. Put WR in front of your app. Write your reducer function later if you ever need it.&lt;/li&gt;
&lt;li&gt;Your building a client-side prototype and don&#39;t want to bother with a real backend, but would love to save a little data somewhere. We&#39;ve got you covered&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;IOT
&lt;ul&gt;
&lt;li&gt;Do you have devices out in the world that are intermittently connected? Maybe you want them to be able to connect to a server and ask &amp;quot;what happened since I last had a connection.&amp;quot; WR is perfect for that.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Business observability
&lt;ul&gt;
&lt;li&gt;Perhaps there are business events you care about on a 3d party platform. These events matter to the business, but not to the software. It would take engineering resources to create webhook endpoints for them. You just want to stick them somewhere and do something with them later.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Games
&lt;ul&gt;
&lt;li&gt;You want multiple players to agree on the state of the world&lt;/li&gt;
&lt;li&gt;Multiple players can send in events&lt;/li&gt;
&lt;li&gt;All players are notified via websocket when the state of the world changes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Personal
&lt;ul&gt;
&lt;li&gt;I want to expose a read endpoint that tells my friends how many unread messages are in my gmail inbox using &lt;a href=&quot;https://developers.google.com/gmail/api/guides/push?utm_campaign=io15&amp;amp;utm_source=gadbc&amp;amp;utm_medium=blog&quot;&gt;the gmail api&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;engineering&quot; tabindex=&quot;-1&quot;&gt;Engineering &lt;a class=&quot;direct-link&quot; href=&quot;#engineering&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On the engineering front, I&#39;ve made the most progress. I&#39;ve added many security measures for the code runner.&lt;/p&gt;
&lt;h3 id=&quot;high-level-architecture-security-and-resilience&quot; tabindex=&quot;-1&quot;&gt;High level architecture, security, and resilience &lt;a class=&quot;direct-link&quot; href=&quot;#high-level-architecture-security-and-resilience&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The system is designed with two priorities&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go down as little as possible&lt;/li&gt;
&lt;li&gt;Don&#39;t let user-generated code do anything bad&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To achieve this, I make heavy use of queueing and isolate the hell out of the VM that runs user-generated code. I took some inspiration from &lt;a href=&quot;https://www.freecodecamp.org/news/running-untrusted-javascript-as-a-saas-is-hard-this-is-how-i-tamed-the-demons-973870f76e1c/&quot;&gt;Tim Nolet&#39;s post&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;flowchart TB&amp;#10;    Client[Client React App]&amp;#10;    HookClient[Endpoint Clients]&amp;#10;    Server&amp;#10;    subgraph isolated-docker&amp;#10;    Runner&amp;#10;    end&amp;#10;    Worker&amp;#10;&amp;#10;    Redis[(Redis)]&amp;#10;    Postgres[(Postgres)]&amp;#10;&amp;#10;    Client &amp;lt;--&amp;gt; Server&amp;#10;    HookClient &amp;lt;--&amp;gt; Server&amp;#10;    Server &amp;lt;--&amp;gt; Redis&amp;#10;    Server &amp;lt;--&amp;gt; Postgres&amp;#10;    Worker &amp;lt;--&amp;gt; Redis&amp;#10;    Worker &amp;lt;--&amp;gt; Postgres&amp;#10;    Worker &amp;lt;--&amp;gt; Runner&amp;#10;&lt;/pre&gt;
&lt;p&gt;When requests hit WebReducer, they are put directly into a &lt;a href=&quot;https://github.com/taskforcesh/bullmq&quot;&gt;bull&lt;/a&gt; queue. The only thing that happens before that is body parsing. If we see a surge in traffic, the database won&#39;t be the point of failure. These enqueued jobs are processed with a reasonably high concurrency. The initial processing doesn&#39;t run the code, it just captures the request in postgres and enqueues the next job to run the code.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;%%{init: {&amp;quot;flowchart&amp;quot;: { &amp;quot;useMaxWidth&amp;quot;: false } }}%%&amp;#10;sequenceDiagram&amp;#10;    participant Client&amp;#10;    participant Server&amp;#10;    participant Redis&amp;#10;    participant IngestRequestWorker as Injest Request Worker&amp;lt;br&amp;gt;( Concurency &amp;#11014;&amp;#65039; )&amp;#10;    participant Postgres&amp;#10;    participant RunnerWorker as Runner Worker&amp;#10;    participant Runner&amp;#10;&amp;#10;    Client -&amp;gt;&amp;gt; Server: Sending request&amp;#10;    Server -&amp;gt;&amp;gt; Redis: Enqueue job for request&amp;#10;    Redis -&amp;gt;&amp;gt; Server: Enqueued&amp;#10;    Server -&amp;gt;&amp;gt; Client: 202&amp;lt;br&amp;gt;(btw, the req id is...)&amp;#10;    Redis -&amp;gt;&amp;gt; IngestRequestWorker: Job&amp;#10;    IngestRequestWorker -&amp;gt;&amp;gt; Postgres: Save this request&amp;lt;br&amp;gt;in a persistent way&amp;#10;    Postgres -&amp;gt;&amp;gt; IngestRequestWorker: &amp;#55357;&amp;#56397;&amp;#10;    IngestRequestWorker -&amp;gt;&amp;gt; Redis: Enqueue a job to&amp;lt;br&amp;gt;actually process&amp;lt;br&amp;gt;this request&amp;#10;    Redis -&amp;gt;&amp;gt; IngestRequestWorker: I got u&amp;#10;    IngestRequestWorker -&amp;gt;&amp;gt; Redis: Finished w/&amp;lt;br&amp;gt; the job btw&amp;#10;    Redis -&amp;gt;&amp;gt; RunnerWorker: Job&amp;#10;    RunnerWorker -&amp;gt;&amp;gt; Postgres: Gimme that req data&amp;#10;    Postgres -&amp;gt;&amp;gt; RunnerWorker: {...}&amp;#10;    RunnerWorker -&amp;gt;&amp;gt; Runner: Hey can you run&amp;lt;br&amp;gt;this code with &amp;lt;br&amp;gt;this payload?&amp;#10;    Runner -&amp;gt;&amp;gt; RunnerWorker: For sure&amp;#10;    RunnerWorker -&amp;gt;&amp;gt; Postgres: Save the new state plz&amp;#10;    Postgres -&amp;gt;&amp;gt; RunnerWorker: Yup&amp;#10;    RunnerWorker -&amp;gt;&amp;gt; Redis: I&amp;#39;m done&amp;#10;&amp;#10;&lt;/pre&gt;
&lt;p&gt;The launcher queue has a concurrency of 1. It&#39;s very important that two launchers are not run at the same time because they would run into a race condition. Whoever saves the state last would win. You might note that a concurrency of 1 is not scalable. To solve this, I&#39;ve created a series of &lt;em&gt;N&lt;/em&gt; launcher queues. When a job is enqueued, it is assigned to one of them by a hash of endpoint key. &lt;em&gt;Individual&lt;/em&gt; endpoints process requests serially, but the system processes requests in parallel.&lt;/p&gt;
&lt;p&gt;The runner itself is stateless. I can create as many instances as I want if it ever becomes the bottleneck.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;flowchart LR&amp;#10;    server[Server]&amp;#10;    capture[Capture Request Job]&amp;#10;&amp;#10;    hash{Hash}&amp;#10;&amp;#10;    subgraph runner-launchers[Runner Launchers - Concurency 1 for each]&amp;#10;        direction TB&amp;#10;        L1[Launcher-1 Queue]&amp;#10;        L2[Launcher-2 Queue]&amp;#10;        L3[Launcher-3 Queue]&amp;#10;        L4[Launcher-4 Queue]&amp;#10;        LN[Launcher-n Queue]&amp;#10;    end&amp;#10;&amp;#10;    runner[Runner]&amp;#10;&amp;#10;    server --&amp;gt; capture&amp;#10;    capture --&amp;gt; hash&amp;#10;    hash --&amp;gt; L1&amp;#10;    hash --&amp;gt; L2&amp;#10;    hash --&amp;gt; L3&amp;#10;    hash --&amp;gt; L4&amp;#10;    hash --&amp;gt; LN&amp;#10;&amp;#10;    L1 --&amp;gt; runner&amp;#10;    L2 --&amp;gt; runner&amp;#10;    L3 --&amp;gt; runner&amp;#10;    L4 --&amp;gt; runner&amp;#10;    LN --&amp;gt; runner&amp;#10;&lt;/pre&gt;
&lt;h3 id=&quot;recomputing-state&quot; tabindex=&quot;-1&quot;&gt;Recomputing state &lt;a class=&quot;direct-link&quot; href=&quot;#recomputing-state&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the best features of WebReducer is going to be recomputing your state when you change the code. But, if you&#39;ve received 1M events, it doesn&#39;t make sense to enqueue 1M jobs in the queue and hit the runner 1M times. I&#39;ve built the system to be capable of batching requests. This could come in handy at some point for high-volume endpoints, but for now, I&#39;m only conceiving of it for replaying state.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zekenie/web-reducer/blob/aeee0b4928b334f84b595971e3eaa36ac1787556/server/src/runner/bulk-runner.service.ts&quot;&gt;bulk update service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;ui&quot; tabindex=&quot;-1&quot;&gt;UI &lt;a class=&quot;direct-link&quot; href=&quot;#ui&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;sticky-date-line&quot; tabindex=&quot;-1&quot;&gt;Sticky date line &lt;a class=&quot;direct-link&quot; href=&quot;#sticky-date-line&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I realized that in my initial design prototype, there was a whole column in the requests table that was often displaying the exact same data:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/wr-update/table.png&quot; alt=&quot;data table with redundant dates&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So I added a line in the table representing the date change. &lt;a href=&quot;https://github.com/zekenie/web-reducer/commit/b51087b5da7af19465cca15dcf901cbca4d35402&quot;&gt;Here&#39;s how I made the date line&lt;/a&gt;. Whoever thought of &lt;code&gt;position: sticky&lt;/code&gt; deserves a medal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/wr-update/moving-table.gif&quot; alt=&quot;data table with slick line&quot; /&gt;&lt;/p&gt;
&lt;!-- https://twitter.com/_off_by_one/status/1473421970985431042 --&gt;
&lt;h4 id=&quot;modals&quot; tabindex=&quot;-1&quot;&gt;Modals &lt;a class=&quot;direct-link&quot; href=&quot;#modals&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I realized that modals are going to be an important UI element, so I setup a system I&#39;m pretty happy with.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/wr-update/modal.png&quot; alt=&quot;data table with slick line&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you want to open an existing component in a react component, here&#39;s how you would do that&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;YourButton&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; openModal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useModals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; openFooModal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fooName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;openModal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; fooName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;openModal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openModal&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Open Foo Modal&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To create a new modal:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; registerModal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../modals/ModalProvider&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  CenteredModalContainer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  ModalBody&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  ModalFooter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  ModalHeader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../modals/Content&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;MODAL_NAME&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MODAL_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MODAL_NAME&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; global &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; Modal &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModalTypes&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MODAL_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MODAL_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Props&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Props&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; text&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;TestModal&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; text &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CenteredModalContainer&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModalHeader&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Are you sure you really want to&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModalBody&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foobar! &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModalBody&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModalFooter&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Footer goes here&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModalFooter&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CenteredModalContainer&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;registerModal&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MODAL_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MODAL_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; TestModal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Features of this system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typed in modal definition and in &lt;code&gt;openModal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Modals are a global concern like URL navigation, so it&#39;s good to have them at the top of the application&lt;/li&gt;
&lt;li&gt;Modals are at the top of the tree, not sprinkled everywhere&lt;/li&gt;
&lt;li&gt;Modals can be launched from anywhere in the tree&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Commits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zekenie/web-reducer/commit/d17f7bdd59c9b70dcf943dc696a6252b09337221#diff-8114314fe68ab7b031b5b38d95e9d9f06f4a897a3f17972a370427e7becbf6a7&quot;&gt;Initial modal type system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zekenie/web-reducer/commit/d17f7bdd59c9b70dcf943dc696a6252b09337221#diff-8114314fe68ab7b031b5b38d95e9d9f06f4a897a3f17972a370427e7becbf6a7&quot;&gt;Modal styling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zekenie/web-reducer/commit/6807e51b8c0788dab041c40bd8610aed39aa1045&quot;&gt;Hygen template for modals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>Things to consider before joining a bootcamp</title>
    <link href="/posts/things-to-consider-before-joining-a-bootcamp/"/>
    <updated>2021-12-28T00:00:00Z</updated>
    <id>/posts/things-to-consider-before-joining-a-bootcamp/</id>
    <content type="html">&lt;p&gt;Bootcamps, bootcamps, bootcamps... They&#39;re everywhere, and everyone has an opinion.&lt;/p&gt;
&lt;p&gt;On one end of the spectrum are the hard-skeptics. The hard-skeptics believe that bootcamps are selling snake oil–it&#39;s impossible to teach what they claim in 3 months. If you went to a bootcamp you were a victim.&lt;/p&gt;
&lt;p&gt;On the other end of the spectrum are the evangelists. In their view, the fact that software engineering bootcamps work proves one of their priors: traditional education is broken, bloated, and corrupt. School has a lot to learn from bootcamps.&lt;/p&gt;
&lt;p&gt;If you&#39;re only exposed to strong opinions, it can be disorienting. It&#39;s challenging to know what to believe. It doesn&#39;t help you &lt;em&gt;want&lt;/em&gt; to believe in bootcamps and there are pushy sales people feeding into your desires.&lt;/p&gt;
&lt;p&gt;I consider myself relatively well informed, if a touch out of date. I was the first instructional hire at an early, highly successful bootcamp. I helped write curriculum. I conducted admissions interviews. I even opened our second campus in Chicago. I firmly believe that we were doing good, interesting things. The vast vast majority of our students got more than we promised. I brought in friends and family as students and staff.&lt;/p&gt;
&lt;p&gt;It worked, basically. That&#39;s not to say it was flawless. There were problems. But, at the end of the day, students were hired as software engineers and are today enjoying successful careers&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;On the other hand... there is &lt;em&gt;absolutely&lt;/em&gt; snake oil on the market, and if you or someone you know is considering attending a bootcamp you should tread carefully. Just because bootcamps can be executed successfully does &lt;em&gt;not&lt;/em&gt; mean the one you are looking at is. Just because a bootcamp was terrific 5 years ago doesn&#39;t mean it is today. If you&#39;re like most people, the tuition is a non-trivial amount of money, and you could &lt;em&gt;absolutely&lt;/em&gt; leave with nothing to show for it.&lt;/p&gt;
&lt;p&gt;So what are you to do? I hope to offer &lt;em&gt;some&lt;/em&gt; guidance.&lt;/p&gt;
&lt;h2 id=&quot;good-bootcamps-have-real-admissions-processes&quot; tabindex=&quot;-1&quot;&gt;Good bootcamps have real admissions processes &lt;a class=&quot;direct-link&quot; href=&quot;#good-bootcamps-have-real-admissions-processes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the biggest red flags to look out for is when there&#39;s no admissions process or when admissions feels more like a sales pitch than an interview. Admissions exists for a reason. Selectively for its own sake is gross, but, selectivity can have a vitally important function: determining if the student is likely to be successful in the program. You want them to be cautious about taking your money.&lt;/p&gt;
&lt;p&gt;There are all kinds of reasons why a student might &lt;em&gt;not&lt;/em&gt; be successful, the biggest of which is that they don&#39;t enjoy programming. Of those students who do enjoy programming, some may not have the space in their life to dedicate to an immersive program. Others might struggle with the format of bootcamps. The list could go on.&lt;/p&gt;
&lt;p&gt;While I believe that everyone has a right to be exposed to technology, and that not enough people have access to tech education, it&#39;s just not the case that you could plop a random person in a room for 3 months and turn them into an entry-level software engineer. &lt;em&gt;There is variance&lt;/em&gt; in the human experience, and a lot of it. The amount of time it takes to go from first line of code to professional engineer varies. The experience level of the applicant pool varies more.&lt;/p&gt;
&lt;p&gt;There&#39;s a lot of BS gatekeeping in tech. I believe that the vast majority of people who like programming can get jobs doing it. But, the path looks different for everyone, and there are a lot of people who aren&#39;t set up to succeed at a bootcamp.&lt;/p&gt;
&lt;p&gt;Good bootcamps screen out students who are not likely to be successful for the sake of the student. Good bootcamps take the attitude, &amp;quot;we&#39;re not going to take your money and your time if we don&#39;t think &lt;em&gt;you&lt;/em&gt; are going to be successful after our program, and in order to know that, we have to spend some serious time talking to &lt;em&gt;you&lt;/em&gt;.&amp;quot;&lt;/p&gt;
&lt;p&gt;If a program does little to no screening, that can mean one of two things. Maybe they have developed a curriculum so good that it can support students from all walks of life and get them all on a level playing field in 3 months... Or, maybe it&#39;s snake oil.&lt;/p&gt;
&lt;p&gt;(A big caveat to this... if a program isn&#39;t making any claims about the job market or outcomes, then maybe this isn&#39;t true. Theoretically, there could be a school just focused on exposing people to programming and adding value in a set period of time. I think &lt;a href=&quot;/posts/we-have-coding-in-schools-backwards/&quot;&gt;this type of thing&lt;/a&gt; would be fantastic for kids.)&lt;/p&gt;
&lt;h2 id=&quot;good-bootcamps-are-serious-about-metrics&quot; tabindex=&quot;-1&quot;&gt;Good bootcamps are serious about metrics &lt;a class=&quot;direct-link&quot; href=&quot;#good-bootcamps-are-serious-about-metrics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What percentage of students are hired after 6 months? When was that data sampled? 2017?? Why?&lt;/p&gt;
&lt;p&gt;Be skeptical of data and ask questions. Good bootcamps treat data gathering a serious task, not a marketing activity.&lt;/p&gt;
&lt;h2 id=&quot;good-bootcamps-hire-some-of-their-own-but-not-too-many&quot; tabindex=&quot;-1&quot;&gt;Good bootcamps hire some of their own, but not too many &lt;a class=&quot;direct-link&quot; href=&quot;#good-bootcamps-hire-some-of-their-own-but-not-too-many&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve seen some bootcamp skeptics say it&#39;s a bad sign when bootcamps hire former students and TAs. Before I worked at a bootcamp, I might have also been somewhat skeptical. But, I&#39;ve seen it work really, really well. We had a smart, talented and energetic student body. They knew our curriculum, and we knew them. Why wouldn&#39;t we hire them? Instructor-alums also remember what it&#39;s like to &lt;em&gt;not&lt;/em&gt; understand something. That can make them very effective teaching partners.&lt;/p&gt;
&lt;p&gt;I worked with instructor-alums who made tremendous contributions to our curriculum (in some cases far more consequential than my own), were dynamic and effective in the classroom and were extremely well liked by students. It&#39;s not a red flag if a school hires former students. I actually think it says something good: people went through this program and didn&#39;t want to leave.&lt;/p&gt;
&lt;p&gt;But, I think there is a precarious balance to strike. When a school relies too heavily on recent alums for its staffing needs, things can become insular. When questions come up about how things work in the &amp;quot;real world&amp;quot; no one knows who to ask. It&#39;s a good idea to ask bootcamps about how many of their staff are alums. Make sure that some of the people teaching you have non-bootcamp software engineering experience.&lt;/p&gt;
&lt;h2 id=&quot;good-bootcamps-strike-a-balance-with-curriculum-updates&quot; tabindex=&quot;-1&quot;&gt;Good bootcamps strike a balance with curriculum updates &lt;a class=&quot;direct-link&quot; href=&quot;#good-bootcamps-strike-a-balance-with-curriculum-updates&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Tech changes really fast. The languages, libraries, and techniques you learn at a bootcamp won&#39;t be the ones you&#39;re using in 5 years, and they might not even be the ones you&#39;re using at your first job. Bootcamps can&#39;t write their curriculum once and be done. They need to keep it current enough. That said, if they&#39;re constantly rewriting it, it can be unstable. Good bootcamps strike a balance. They make curriculum updates often enough that the lessons aren&#39;t stale, but not so often that every cohort is in alpha mode.&lt;/p&gt;
&lt;h2 id=&quot;good-bootcamps-know-what-they-aren&#39;t&quot; tabindex=&quot;-1&quot;&gt;Good bootcamps know what they aren&#39;t &lt;a class=&quot;direct-link&quot; href=&quot;#good-bootcamps-know-what-they-aren&#39;t&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bootcamps are not a replacement for computer science programs or college. They have very different ends. Computer Science is an academic discipline. Computer Scientists are in the business of discovery. Bootcamps are entirely industry-focused. At a great bootcamp you learn to assess computational complexity and get a solid overview of data structures and algorithms, but it&#39;s not a CS degree. Sadly, many students who graduate from &lt;em&gt;bad&lt;/em&gt; CS programs do so with less practical understanding of Computer Science than students who went to a &lt;em&gt;great&lt;/em&gt; bootcamp. I taught several CS grads/dropouts (from pretty good undergrad programs!) who were not planning on continuing on in their academic CS career and also weren&#39;t job ready.&lt;/p&gt;
&lt;p&gt;Bootcamps are not a panacea. They are not the solution to every problem you can think of. You can&#39;t take a random laid-off worker, stick them in a bootcamp, and expect consistently good results.&lt;/p&gt;
&lt;h2 id=&quot;why-do-bootcamps-work-anyway&quot; tabindex=&quot;-1&quot;&gt;Why do bootcamps work anyway? &lt;a class=&quot;direct-link&quot; href=&quot;#why-do-bootcamps-work-anyway&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve sometimes joked that our school could have been just as successful if we had no curriculum. This is an intentionally provocative statement, but I really wonder if it&#39;s true.&lt;/p&gt;
&lt;p&gt;Most of our success can be explained by a few factors.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our students were highly motivated. They had put their lives on hold and were all-in.&lt;/li&gt;
&lt;li&gt;Staff was bought in. We were right in there with the students, so much so that we risked burning ourselves out.&lt;/li&gt;
&lt;li&gt;Our students were ready for what we were teaching (see admissions above). To get in you needed to be able to recurse through several data structures in your language of choice. Day 1 wasn&#39;t &amp;quot;Intro to Variables&amp;quot; it was &amp;quot;Let&#39;s write our own hash table&amp;quot;&lt;/li&gt;
&lt;li&gt;Students&#39; play time wasn&#39;t rate-limited. Unlike in so many fields, they didn&#39;t need to wait for any external parties to get feedback. Students never waited for me to grade their programs to see if they worked. The computer told them it worked!&lt;/li&gt;
&lt;li&gt;We could get them &lt;a href=&quot;/posts/how-to-help-a-student-get-unstuck&quot;&gt;unstuck&lt;/a&gt;. Getting stuck is one of the biggest obstacles to learning how to code. It&#39;s hard for a student to know the difference between productive and unproductive struggle. When you spin your wheels for too long, your motivation starts to degrade. Once that happens, you&#39;re done.&lt;/li&gt;
&lt;li&gt;The community was supportive and motivating. If you were in a rut, you could look around and see peers that were on the same journey. You weren&#39;t alone.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;A lot has changed since I left in 2017, so I can&#39;t say I&#39;m well informed enough to speak to any specific bootcamps&#39; quality, today. That&#39;s why I&#39;m not naming them. But I can say that they &lt;em&gt;can&lt;/em&gt; and &lt;em&gt;do&lt;/em&gt; sometimes work. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Lessons learned from a recent job search</title>
    <link href="/posts/lessons-learned-from-a-recent-job-search/"/>
    <updated>2022-01-23T00:00:00Z</updated>
    <id>/posts/lessons-learned-from-a-recent-job-search/</id>
    <content type="html">&lt;p&gt;Toward the end of 2021, I was one of the countless tech workers who left one job and took another. The process surprised me in a number of ways. I needed to update my priors about a few things, and by writing this, maybe I&#39;ll update yours too.&lt;/p&gt;
&lt;p&gt;First, I thought it would be easy&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. My reasoning was as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I know how hard it is to hire great engineers&lt;/li&gt;
&lt;li&gt;I know I&#39;m a good engineer..&lt;/li&gt;
&lt;li&gt;Everyone&#39;s desperate to fill roles...&lt;/li&gt;
&lt;li&gt;So it should be a walk in the park.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It turns out it wasn&#39;t. It wasn&#39;t radically hard, but it required significant thought and energy. In the middle of the search, I got kind of pessimistic... And yet, in the end, it was successful both in terms of the total number of offers received and their dollar values.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/job-search/timeline.png&quot; alt=&quot;Graphical representation of the timeline and my emotions&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve tried to capture some of my learnings in a form that may be useful to you. Take everything I say with a grain of salt. Everyone&#39;s experience is their own, and this may not apply to you. Also the TTL on any post like this is probably pretty short.&lt;/p&gt;
&lt;h2 id=&quot;understand-that-the-system-is-overloaded&quot; tabindex=&quot;-1&quot;&gt;Understand that the system is overloaded &lt;a class=&quot;direct-link&quot; href=&quot;#understand-that-the-system-is-overloaded&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We said that 2020 was the year that broke every time-series chart and 2021 said &amp;quot;hold my beer.&amp;quot; Everything&#39;s overloaded, and the job search infrastructure is no exception. In-house recruiters I spoke with had a hoarse voices, were late to meetings and occasionally confused me with other candidates. And I don&#39;t blame them. They are dealing with unprecedented pressure from their employers, while at the same time they have more applications than they can process.&lt;/p&gt;
&lt;p&gt;It&#39;s almost like a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/architecture/antipatterns/retry-storm/&quot;&gt;Retry Storm&lt;/a&gt;. Companies are so overloaded with applicants, that end up giving them a subpar experience. Employer latency causes applicants to freak out and put out more applications, recursively exacerbating the problem.&lt;/p&gt;
&lt;p&gt;My advice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take a deep breath. You&#39;ll feel compelled to draw conclusions based on incomplete information, choose not to. Silence from companies can be harder to deal with than rejection because it begs us to read into tea leaves. Just pause. Conclusions will come in time.&lt;/li&gt;
&lt;li&gt;Recruiters are tired, give them some grace. They&#39;re trying. Some of them are new on the job themselves. Others just had their coworkers quit. Try not to take it personally.&lt;/li&gt;
&lt;li&gt;It&#39;s ok to politely check in. As with all things, there&#39;s a right timing and dosage.&lt;/li&gt;
&lt;li&gt;Be gracious in cases of rejection. It doesn&#39;t matter how great you are, you&#39;re not a good fit everywhere. And even when you are, misreads happen. Maybe they&#39;re making a mistake, or maybe they&#39;re saving you from one. There are more fish in the sea.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;network&quot; tabindex=&quot;-1&quot;&gt;Network &lt;a class=&quot;direct-link&quot; href=&quot;#network&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve heard it said that most jobs are filled through networking... I don&#39;t know if that&#39;s been rigorously established, but it&#39;s the kind of statement that just &lt;em&gt;feels&lt;/em&gt; true. When I started my search, I decided that I didn&#39;t want to rely solely on my existing contacts for introductions because I didn&#39;t want to limit myself to my known universe. I wanted to branch out. Broadly speaking this was a waste of my time. Most of the companies that I applied to without a contact never wrote back to me, or wrote back so late that they were so hopelessly behind that it was pointless to consider them.&lt;/p&gt;
&lt;p&gt;I expected networking to be effective, but I was surprised by the degree. Given how overloaded the system is, networks allow you to punch through the noise and get in front of the right people. I wouldn&#39;t disregard conventional wisdom on this one. Use the networks you have.&lt;/p&gt;
&lt;h2 id=&quot;learn-from-your-cover-letters&quot; tabindex=&quot;-1&quot;&gt;Learn from your cover letters &lt;a class=&quot;direct-link&quot; href=&quot;#learn-from-your-cover-letters&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I was surprised how many cover letters I had to write, even with a warm introduction. Most companies asked me to write a cover letter. I knew it wasn&#39;t going to be read closely, but it required non-negligible attention from me. I didn&#39;t want my cover letters to stand out in a negative way&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;div class=&quot;flex flex-row justify-center&quot;&gt;
  &lt;img class=&quot;sm:w-2/3&quot; src=&quot;/img/job-search/cover-letters.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;I observed a lot of variance in the effort required for me to write a cover letter. Sometimes an articulate and crisp cover letter just fell out of my mind. Other times it was like pulling teeth. I started recording the subjective effort required to write a cover letter. I realized that the ease with which I wrote a cover letter was a decent proxy for &lt;em&gt;something&lt;/em&gt;. If I couldn&#39;t articulate in a cover letter why a company was write for me, maybe that&#39;s a sign that I&#39;d struggle in an interview. Maybe it&#39;s a sign that I&#39;d be hard to tell my friends and family about the company once I start. Maybe it&#39;s a bad fit.&lt;/p&gt;
&lt;p&gt;Like with all metrics, it&#39;s imperfect! But it&#39;s something.&lt;/p&gt;
&lt;h2 id=&quot;take-homes&quot; tabindex=&quot;-1&quot;&gt;Take-homes &lt;a class=&quot;direct-link&quot; href=&quot;#take-homes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Take-home projects don&#39;t seem to be quite as ubiquitous as they were a few years ago, but I still had to do quite a few of them. Often it only takes a couple of hours to meet the bare minimum requirements, but require significantly more effort to stand out. This presents some unfortunate systemic biases. For example, if you spend a lot of time taking care of a family member, and are doing a job search while working full-time, it&#39;s &lt;em&gt;extremely&lt;/em&gt; difficult to find the time or energy to do take-home work well.&lt;/p&gt;
&lt;p&gt;I saw a couple of companies pay me to do a take-home assessment, which I appreciated. It was a signal that they valued my time investment.&lt;/p&gt;
&lt;p&gt;Even though the system is pretty unfair and imperfect, I have some pointers for how you can stand out in take-homes that I think are worth sharing.&lt;/p&gt;
&lt;h3 id=&quot;pick-something-to-nerd-out-on&quot; tabindex=&quot;-1&quot;&gt;Pick something to nerd out on &lt;a class=&quot;direct-link&quot; href=&quot;#pick-something-to-nerd-out-on&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Looking for a way to go above and beyond? Pick some aspect of the problem and take it to the next level.&lt;/p&gt;
&lt;p&gt;Maybe you&#39;re being asked to write a program that reads &lt;code&gt;stdin&lt;/code&gt;, does some processing, and writes to &lt;code&gt;stdout&lt;/code&gt;. It&#39;d be trivial to do the work in memory, but you could decide that you want to stream the input and output so that your program supports arbitrarily large input sizes.&lt;/p&gt;
&lt;p&gt;Maybe you decide you want to go a step further and load test it. Perhaps you can cover an edge case in an interesting way?&lt;/p&gt;
&lt;p&gt;I found that take-homes often fell into one of a few buckets&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data processing script&lt;/li&gt;
&lt;li&gt;UI&lt;/li&gt;
&lt;li&gt;Fullstack app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you do one, you can start to borrow little flourishes from one and use them on others. The problems are different enough that you can&#39;t copy meaningful code, but you can absolutely borrow structure, testing techniques, and sparkles from one project to another.&lt;/p&gt;
&lt;h3 id=&quot;have-a-good-readme&quot; tabindex=&quot;-1&quot;&gt;Have a good readme &lt;a class=&quot;direct-link&quot; href=&quot;#have-a-good-readme&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Spend more time than you think on a readme. It may seem ridiculous to document a silly sample project, but it will show that you care about the job you&#39;re applying to and demonstrate an ability to write for humans.&lt;/p&gt;
&lt;p&gt;Talk about what you did, how to run it and what went into your design decisions. If you&#39;re being asked to build a UI, include screenshots–many of the people reviewing your code won&#39;t have the time to fire it up. For non-ui take-homes, include sample output. The point is that you want someone who &lt;em&gt;only&lt;/em&gt; sees the readme to understand that you did a good, careful job.&lt;/p&gt;
&lt;h3 id=&quot;use-diagrams&quot; tabindex=&quot;-1&quot;&gt;Use diagrams &lt;a class=&quot;direct-link&quot; href=&quot;#use-diagrams&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Take-homes offer an opportunity to show how you think and communicate visually.&lt;/p&gt;
&lt;p&gt;I used &lt;a href=&quot;https://monodraw.helftone.com/&quot;&gt;Monodraw&lt;/a&gt; to create ascii art diagrams in my code and my readme. I got a lot of comments about it from interviewers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/_off_by_one/status/1423342594604601345&quot;&gt;https://twitter.com/_off_by_one/status/1423342594604601345&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Other nice diagramming tools could be &lt;a href=&quot;https://mermaid-js.github.io/mermaid/#/&quot;&gt;Mermaid&lt;/a&gt; or &lt;a href=&quot;https://excalidraw.com/&quot;&gt;Excalidraw&lt;/a&gt; (&lt;a href=&quot;/posts/why-is-excalidraw-so-good/&quot;&gt;I have feelings&lt;/a&gt; about Excalidraw).&lt;/p&gt;
&lt;h3 id=&quot;have-tests&quot; tabindex=&quot;-1&quot;&gt;Have tests &lt;a class=&quot;direct-link&quot; href=&quot;#have-tests&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Write automated tests. I believe that most companies will feel that a lack of tests reflects poorly on a candidate. Focus on tests that are readable and help them understand how you think about the problem space. Great tests read like documentation.&lt;/p&gt;
&lt;h3 id=&quot;validate-inputs&quot; tabindex=&quot;-1&quot;&gt;Validate inputs &lt;a class=&quot;direct-link&quot; href=&quot;#validate-inputs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Good software validates inputs at its perimeter. Sometimes companies will throw test data at your code that they didn&#39;t share with you. If there are errors, that&#39;s not always a bad thing, but they should be semantic and sensible. You should codify the assumptions you&#39;re making not just with tests, but with input validation.&lt;/p&gt;
&lt;h3 id=&quot;use-modern-tech&quot; tabindex=&quot;-1&quot;&gt;Use modern tech &lt;a class=&quot;direct-link&quot; href=&quot;#use-modern-tech&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Don&#39;t pull out old libraries and tools. Use the new stuff. That may seem intimidating if you aren&#39;t familiar with the latest tooling. Just pick tools that are modern &lt;em&gt;and&lt;/em&gt; minimalist, of which there are many. These tools are also easier to read and understand if the interviewer &lt;em&gt;also&lt;/em&gt; doesn&#39;t know them. It&#39;s worth the small time investment to find a cool new library rather than pulling out the same one you used on your last job search.&lt;/p&gt;
&lt;h2 id=&quot;negotiation&quot; tabindex=&quot;-1&quot;&gt;Negotiation &lt;a class=&quot;direct-link&quot; href=&quot;#negotiation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;m not an expert in salary negotiation. I&#39;ll still share the general framework I used.&lt;/p&gt;
&lt;h3 id=&quot;&amp;quot;what-are-your-expectations-for-salary&amp;quot;&quot; tabindex=&quot;-1&quot;&gt;&amp;quot;What are your expectations for salary?&amp;quot; &lt;a class=&quot;direct-link&quot; href=&quot;#&amp;quot;what-are-your-expectations-for-salary&amp;quot;&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many companies asked me about my salary expectations. I said something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Look, this is an unusual time. I&#39;m actually not sure what a fair market rate is for me. I&#39;ve heard of people with similar experience being offered between X and Y&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; and so I expect I&#39;ll get offers in that range.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some companies just took that information. Others expressed concern because they knew they could not make offers in the high end of my range. In response I told them that I wasn&#39;t sure I&#39;d be getting offers that high, and that my range was intentionally large. I also told them that base salary was not the only thing that I cared about and that I would be making a nuanced decision based on many factors.&lt;/p&gt;
&lt;h3 id=&quot;lining-up-offer-timelines&quot; tabindex=&quot;-1&quot;&gt;Lining up offer timelines &lt;a class=&quot;direct-link&quot; href=&quot;#lining-up-offer-timelines&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At the beginning of the process I picked a date at which point I was hoping to conclude the process. Whenever I had initial conversations with companies I told them this date. Because companies got back to me at different times, sometimes this added a little bit too much pressure, and I ended up withdrawing from the process with some.&lt;/p&gt;
&lt;p&gt;After each stage of interview, I tried to refine my understanding of when I might be receiving an offer. Once I knew might be getting an offer from a company, I told other companies about it. I said something like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey &lt;code&gt;first-name&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;I wanted to let you know that I&#39;m expecting (🤞) to get an offer from another company in the middle of next week. I&#39;ve really enjoyed the process with &lt;code&gt;your-company&lt;/code&gt; so far, and if it doesn&#39;t work out, I would hate for interview logistics to be the reason why. I know I said I was looking to make a final decision by &lt;code&gt;x&lt;/code&gt;, but do you think it would be possible to move that up a little bit? Maybe we could conclude the process by &lt;code&gt;y&lt;/code&gt;? If this just isn&#39;t logistically possible, I totally get it, and I&#39;ll do what I can to make things line up. I really appreciate it!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This approach worked every time and was 100% truthful. I would &lt;em&gt;not&lt;/em&gt; recommend doing something like this if you&#39;re not expecting an offer just to create FOMO.&lt;/p&gt;
&lt;h3 id=&quot;comparing-offers&quot; tabindex=&quot;-1&quot;&gt;Comparing offers &lt;a class=&quot;direct-link&quot; href=&quot;#comparing-offers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is genuinely hard because predicting the future is hard. If I had gotten red flags from any companies, I would have taken them out of the running, but all the companies I got offers from seemed compelling in one way or another.&lt;/p&gt;
&lt;p&gt;I tried to assign a monetary value to everything in a spreadsheet. I quantified uncertainty (bonus, stock performance, etc) as best I could. It was abundantly clear which offer had the highest dollar value. I then asked myself, &amp;quot;do I like any of the other companies enough to &lt;em&gt;pay&lt;/em&gt; $&lt;code&gt;x&lt;/code&gt; to work there instead of &lt;code&gt;company&lt;/code&gt;?&amp;quot; I framed it this way to myself because a dollar not earned is the same as a dollar spent. In my case, it worth paying $&lt;code&gt;x&lt;/code&gt;. After telling the other companies about the highest offer, and them not being able to quite meeting it, I took the highest offer I got. That isn&#39;t the right choice for everyone in every situation, but it was for me this time.&lt;/p&gt;
&lt;p&gt;I&#39;m a few months in, and so far I&#39;m pretty happy! I didn&#39;t feel like taking the highest offer was a sacrifice in any way. I wasn&#39;t selling my soul. I felt good enough about all the companies and that gave me permission to make it mostly about money.&lt;/p&gt;
&lt;h2 id=&quot;in-summary&quot; tabindex=&quot;-1&quot;&gt;In summary &lt;a class=&quot;direct-link&quot; href=&quot;#in-summary&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a great time to switch jobs if you&#39;re feeling the itch. Companies are &lt;em&gt;hungry&lt;/em&gt; for talent and you can absolutely do really well in a search. But, if your results will vary and I think you&#39;ll find that the outcome will mirror the effort you put in. In my case it required some real heavy lifting.&lt;/p&gt;
&lt;p&gt;Figure out where your effort can yield the best results and lean into it.&lt;/p&gt;
&lt;p&gt;Try to learn about yourself in the process. You may end up with several offers, and there could be subtle cues from your application process that could come in handy. Journal, make spreadsheets, talk to friends.&lt;/p&gt;
&lt;p&gt;Communicate openly and respectfully with the companies as the process progresses. Sometimes the truth is the best negotiation technique. Do what you can to make the truth work for you.&lt;/p&gt;
&lt;p&gt;When it&#39;s all said and done, take care of yourself. This process is exhausting. If you can, take a few weeks off.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;My expectations were in part shaped by the fact that my last 2 job searches were short and sweet. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;In the past when I&#39;ve reviewed job applications, I&#39;ve more than once seen cover letters addressed to the wrong company! &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;X and Y were a wide range! &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>Reflections on babies and how they fuck up household supply chains</title>
    <link href="/posts/baby-household-supply-chain/"/>
    <updated>2022-05-18T00:00:00Z</updated>
    <id>/posts/baby-household-supply-chain/</id>
    <content type="html">&lt;p&gt;Having a child is a wonderful, life altering experience. It is profoundly fulfilling and changes you physically, cognitively, and emotionally. Someday maybe I&#39;ll have enough distance from it to have something more interesting to say about the emotional part of parenthood, but today is not that day. Today I&#39;m talking about logistics.&lt;/p&gt;
&lt;p&gt;What they say is true: having kids is hard. It&#39;s hardest on the birthing parent, but it&#39;s hard for everyone. The nitty gritty, hands-on-kid childcare happens round-the-clock, and takes a lot of work. But, you&#39;re interacting with someone you love.&lt;/p&gt;
&lt;p&gt;There&#39;s another kind of work that&#39;s underestimated by first-time parents (at least by this one): &lt;strong&gt;household supply chain&lt;/strong&gt;. It&#39;s just as necessary as any part of childcare, but it&#39;s less rewarding than spending time with your new kid.&lt;/p&gt;
&lt;p&gt;Stuff needs to get purchased, moved around, cleaned, disposed of, returned, repaired, sold, replaced, etc. This kind of work is always happening in a home, but having a kid absolutely overloads your finite capacity to manage it. You have the number of adult hands you have&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. But the throughput of the system goes through the roof. Before you know it, you&#39;re living in a pile of dishes, boxes and laundry, asking &amp;quot;what happened&amp;quot; and wondering if FEMA can help.&lt;/p&gt;
&lt;h2 id=&quot;accumulation-of-objects-under-management&quot; tabindex=&quot;-1&quot;&gt;Accumulation of Objects Under Management &lt;a class=&quot;direct-link&quot; href=&quot;#accumulation-of-objects-under-management&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/baby-supply-chain/workflow.png&quot; alt=&quot;Household supplychain workflow&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Everything in your home–from furniture to a container of takeout–is an object you&#39;re managing. It entered your home because you or someone else thought you needed it. It will leave your home someday. While it&#39;s under your care, it&#39;s accumulating costs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Storage cost (unit = &lt;code&gt;m^2&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Cognitive cost (i.e. knowing that it exists, where it is, if it needs maintenance, etc; units = &lt;code&gt;squishy&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Maintenance cost (examples includes washing dishes, doing laundry, having something repaired, etc; units = &lt;code&gt;time, money&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the normal course of life, objects are entering and leaving your home at a somewhat predictable rate. Sure, you have surges of activity (for example when you move, do spring cleaning, have a big life event, etc). But, your object management is spread out over time. When you have kids you have 10,000 new problems to solve all at once. You need to figure out what to purchase to solve the problem, how to use it, return what didn&#39;t work, figure out how to maintain what you keep. No one can tell you a definitive shopping list because every baby, parent, and home is different.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/baby-supply-chain/flow-of-OUM.png&quot; alt=&quot;The flow of new objects peaks with a new kid&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To make matters worse, your kid grows out of most items quickly, so there are aftershocks to the initial supply chain earthquake.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/baby-supply-chain/lifecycle-timeline.png&quot; alt=&quot;Different objects have different lifecycles, baby stuff moves fast&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;negative-spirals-(how-assets-become-liabilities)&quot; tabindex=&quot;-1&quot;&gt;Negative spirals (how assets become liabilities) &lt;a class=&quot;direct-link&quot; href=&quot;#negative-spirals-(how-assets-become-liabilities)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are some negative feedback loops that can happen when you get behind on object maintenance. If everything is clean and put away, you can find it. When you need a burp cloth, or a clean shirt, or both–you know what drawers to open. It&#39;s an &lt;code&gt;o(1)&lt;/code&gt; operation. When you have piles of clean laundry, you&#39;re looking through everything and it&#39;s an &lt;code&gt;o(n)&lt;/code&gt; operation. In the worst case scenario your dresser itself becomes a derelict object, containing nothing you need and taking up space.&lt;/p&gt;
&lt;p&gt;You can get into a downward spiral. The influx of problems causes an influx of new objects. The influx of new objects strains the organizational systems you have in place. When nothing has a home, you&#39;re spending more cycles on searching for objects. At the same time, your capacity to work, organize, and maintain is tanking.&lt;/p&gt;
&lt;h2 id=&quot;some-misc-notesrants-on-babies-and-parenthood&quot; tabindex=&quot;-1&quot;&gt;Some misc notes/rants on babies and parenthood &lt;a class=&quot;direct-link&quot; href=&quot;#some-misc-notesrants-on-babies-and-parenthood&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our society is not set up for raising children. It&#39;s &lt;em&gt;hard&lt;/em&gt;. Even with paid parental leave, even with money, even when you&#39;re ready. Childcare is radically expensive. You can&#39;t get any fucking baby formula. We&#39;re two years into a plague–unprecedented in modern times–and the government is sitting on their hands approving life saving vaccines for kids because.....&lt;/p&gt;
&lt;p&gt;It&#39;s totally worth it. If you want to have kids, you should go for it because nothing else is quite like it. I just can&#39;t write something about babies without saying:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It should be a choice. Abortion should be legal, de-stigmatized, safe and easy to access.&lt;/li&gt;
&lt;li&gt;We can afford, as a society, to make it a little easier on babies and new parents.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;When you have a kid, this capacity is greatly reduced because one of you is doing childcare at all hours. Also, the birthing parent is recovering for weeks which greatly reduces their capacity. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  
  <entry>
    <title>WebReducer Update 3</title>
    <link href="/posts/web-reducer-update-3/"/>
    <updated>2022-05-31T00:00:00Z</updated>
    <id>/posts/web-reducer-update-3/</id>
    <content type="html">&lt;p&gt;It&#39;s been a little while since my &lt;a href=&quot;https://offbyone.us/posts/micro-business-update/&quot;&gt;last update&lt;/a&gt; on WebReducer. Let&#39;s just say I&#39;ve had &lt;a href=&quot;https://offbyone.us/posts/baby-household-supply-chain/&quot;&gt;a lot going on&lt;/a&gt; 😅.&lt;/p&gt;
&lt;p&gt;Progress on WebReducer has continued in fits and starts none the less. I&#39;ve been shockingly productive given how little time I&#39;ve spent at the keyboard. During parental leave, I&#39;ve treated this side project like many people would treat reading a book. When I have a few minutes, I pop it open. Reliably I get 30 minutes, twice a week while I&#39;m getting allergy shots. I also find 5m - 1h here and there at all hours of the day and night.&lt;/p&gt;
&lt;p&gt;But, there are many unstructured hours every day for my mind to wander. My hands are tied up, but I can think about anything. Sometimes I think about my son, sometimes I think about something that has to get done around the house, and sometimes I think about WebReducer.&lt;/p&gt;
&lt;p&gt;This has had a pretty interesting effect on the work. I&#39;m much, much more deliberate. Instead of messing around and trying things, by the time I get to the keyboard, I know exactly what I want to do. I&#39;ve already considered 3 different approaches, and I&#39;ve made my preliminary decision. Of course, there are changes I make once I see how things play out, but I generally have a pretty good sense of my system design by the time I try to build it. That&#39;s not to say I have a formal plan, in fact, nothing could be farther from the truth, just a slow-cooked mental model of the direction I&#39;m going.&lt;/p&gt;
&lt;h2 id=&quot;so-what-has-happened&quot; tabindex=&quot;-1&quot;&gt;So, what has happened? &lt;a class=&quot;direct-link&quot; href=&quot;#so-what-has-happened&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;first-time-ux&quot; tabindex=&quot;-1&quot;&gt;First-time UX &lt;a class=&quot;direct-link&quot; href=&quot;#first-time-ux&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/img/wr3/requests-empty-state.gif&quot; alt=&quot;Requests table empty state&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you first open up a WebReducer, you should be able to get up and running &lt;em&gt;fast&lt;/em&gt;. Like in a click or two. I built this snazzy little empty-state that lets you configure your first request to the endpoint and copy it to your clipboard in a single click. Here&#39;s a janky demo of that working locally.&lt;/p&gt;
&lt;div class=&quot;video-wrapper&quot;&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/pjBO7wkZNCA&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I like this interface because it gets you into the product without even writing a line of code. I imagine that after you make your first request and see it appear, you&#39;ll somehow be prompted to write some code, perhaps by a cursor starting to write some for you... TBD.&lt;/p&gt;
&lt;h3 id=&quot;authentication&quot; tabindex=&quot;-1&quot;&gt;Authentication &lt;a class=&quot;direct-link&quot; href=&quot;#authentication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the most important things about WebReducer is that you can use it without an account. When someone goes to webreducer.dev, they already have an endpoint that they can &lt;code&gt; $ curl&lt;/code&gt;. This endpoint is ephemeral, and to keep it, you need to sign up.&lt;/p&gt;
&lt;p&gt;It is also important that I don&#39;t store passwords. I think passwords are a barrier to entry and a security risk. I want magic link authentication. Eventually there can be a 2FA option (which could be a password, authentication app, etc). For the MVP I don&#39;t think this is necessary.&lt;/p&gt;
&lt;p&gt;There are 2 parts of WebReducer authentication: &lt;em&gt;sign-in tokens&lt;/em&gt; and &lt;em&gt;credentials&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Credentials are given to all users–authenticated or otherwise. The second you hit the app, you have a guest account that&#39;s not associated with any email address. Once you sign up, we send over a sign-in token to your email address. When you go to the URL we send, we consider to be a &amp;quot;real&amp;quot; user. Sign-in tokens exist to prove you control an email address.&lt;/p&gt;
&lt;p&gt;Credentials are made up of a short-lasting &lt;em&gt;JSON web token&lt;/em&gt; and a long lasting &lt;em&gt;refresh token&lt;/em&gt;. Both are sent along with every request you make. JWTs can be validated statelessly by multiple independent micro-services, and since the app is relatively chatty this has certain advantages. We don&#39;t need to do a db hit on every request or do any kind of shared session state. But, when the JWT expires, the refresh token is &lt;a href=&quot;https://github.com/zekenie/web-reducer/blob/16ca0873c6c380d761178bbc82b2858cb9df83ac/server/src/auth/auth.service.ts#L53&quot;&gt;used&lt;/a&gt; to issue a new JWT/refresh token pair. The refresh token is stateful–it exists in the database. Refresh tokens can only be used once.&lt;/p&gt;
&lt;p&gt;To sum up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can use the app without signing up&lt;/li&gt;
&lt;li&gt;Once you sign up, you never need a password&lt;/li&gt;
&lt;li&gt;You don&#39;t get logged out all the time&lt;/li&gt;
&lt;li&gt;I can spin up microservices that can use this auth scheme without holding centralized state&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;secrets&quot; tabindex=&quot;-1&quot;&gt;Secrets 🤫 &lt;a class=&quot;direct-link&quot; href=&quot;#secrets&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/img/wr3/secrets-ui.gif&quot; alt=&quot;Secrets interface&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I realized that many, many webhook endpoints don&#39;t work without stored secrets. Often, webhook providers want to you validate the authenticity of a message using &lt;a href=&quot;https://en.wikipedia.org/wiki/HMAC&quot;&gt;hash-based message authentication code&lt;/a&gt; (HMAC). HMAC requires you to have a secret in your possession that was given to you by the provider. It&#39;d be terrible for me to require users to just keep that secret in plain text in their code. I don&#39;t plan on having my database breached, but if I did, I&#39;d want it to be harder than that to access this kind of secret.&lt;/p&gt;
&lt;p&gt;I decided to build a separate microservice in charge of secrets.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;flowchart LR&amp;#10;    Client[Remix App]&amp;#10;    Browser&amp;#10;    HookClient[Endpoint Clients]&amp;#10;    subgraph privateNetwork[Private Network]&amp;#10;&amp;#10;        Server&amp;#10;        Redis[(Redis)]&amp;#10;        Postgres[(Postgres)]&amp;#10;        SecretsDb[(SecretsDb)]&amp;#10;        Worker&amp;#10;        Secrets[Secrets Service]&amp;#10;        subgraph isolated-docker&amp;#10;            Runner&amp;#10;        end&amp;#10;    end&amp;#10;&amp;#10;    click Client &amp;quot;https://github.com/zekenie/web-reducer/tree/main/client#readme&amp;quot;&amp;#10;    click Server &amp;quot;https://github.com/zekenie/web-reducer/tree/main/server#readme&amp;quot;&amp;#10;    click Worker &amp;quot;https://github.com/zekenie/web-reducer/tree/main/server#readme&amp;quot;&amp;#10;    click Secrets &amp;quot;https://github.com/zekenie/web-reducer/tree/main/secrets#readme&amp;quot;&amp;#10;    click Runner &amp;quot;https://github.com/zekenie/web-reducer/tree/main/runner#readme&amp;quot;&amp;#10;&amp;#10;&amp;#10;    Client &amp;lt;--&amp;gt; Server&amp;#10;    Server --&amp;gt; Secrets&amp;#10;    HookClient &amp;lt;--&amp;gt; Client&amp;#10;    Secrets &amp;lt;--&amp;gt; SecretsDb&amp;#10;    Server &amp;lt;--&amp;gt; Redis&amp;#10;    Server &amp;lt;--&amp;gt; Postgres&amp;#10;    Worker &amp;lt;--&amp;gt; Redis&amp;#10;    Worker &amp;lt;--&amp;gt; Postgres&amp;#10;    Worker &amp;lt;--&amp;gt; Runner&amp;#10;    Browser &amp;lt;--&amp;gt; Client&amp;#10;    Redis&amp;#10;&amp;#10;classDef selected stroke-width:4px;&amp;#10;class Secrets selected;&amp;#10;&lt;/pre&gt;
&lt;p&gt;The organizing principal behind this decision is that if any single part of the system is breached, secrets are not compromised.&lt;/p&gt;
&lt;p&gt;Secrets are organized in namespaces. A namespace is simply a collection of secrets with an access key. There is no way to list access keys, the Secret Service doesn&#39;t even have them, it just stores a hash digest. When a WebReducer is made, there is a namespace created, and the access key is stored encrypted in the monolith. If the monolith db is breached, an attacker would also need an encryption secret which is stored in an environment variable.&lt;/p&gt;
&lt;p&gt;If the secret database itself was breached, it would be hard for an attacker to exploit. Every key value pair is encrypted with a per-namespace key, and these keys are encrypted with a key that&#39;s stored in an environment variable.&lt;/p&gt;
&lt;p&gt;I&#39;m working on automatic key rotation for all these systems, but it&#39;s not there yet.&lt;/p&gt;
&lt;p&gt;I&#39;m &lt;em&gt;sure&lt;/em&gt; there are flaws in this system. I&#39;m not trying to launch this product with enterprise-grade security. I&#39;m trying to not be irresponsible as I learn if this product has legs. I think if people start using it, it&#39;ll get better. It&#39;s open source. There will eventually be a bug bounty program. If this product is meant to be, security will come. Customers will be warned.&lt;/p&gt;
&lt;h3 id=&quot;new-ideas&quot; tabindex=&quot;-1&quot;&gt;New ideas &lt;a class=&quot;direct-link&quot; href=&quot;#new-ideas&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here&#39;s an incomplete list of feature ideas. Some of these I may get to, some I may not. Some are distractions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Side effects&lt;/strong&gt;. WebReducer code is synchronous and functionally pure. With the same input, and the same code, you get the same output. There is currently no communication with any outside system. A side effects feature would allow users to trigger requests to other systems &lt;em&gt;after&lt;/em&gt; the reducer runs. Unlike the reducer, side effect code would not re-run with code changes. Side effects could be an email you conditionally send to yourself when the state changes in a certain way, an http request you send to a slack endpoint to notify a channel, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State branches&lt;/strong&gt;. You should be able to have a single WebReducer control different versions of your state. You could map different write keys to different state branches&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheduled requests&lt;/strong&gt;. What if you could set up a cron to hit your own WebReducer. Could be very useful for some applications&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queryable views&lt;/strong&gt;. The original WebReducer concept had &lt;code&gt;readKeys&lt;/code&gt; and &lt;code&gt;writeKeys&lt;/code&gt; These were effectively revokable endpoints that could read and write state. &lt;code&gt;writeKeys&lt;/code&gt; couldn&#39;t mutate state directly, instead state changes were mediated by the &lt;code&gt;reducer&lt;/code&gt; that users wrote. I realized that it could be great to have the read behavior also be configurable. Perhaps there&#39;s a &lt;code&gt;readState&lt;/code&gt; function that is called on every &lt;code&gt;readKey&lt;/code&gt; request.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;demo-ideas&quot; tabindex=&quot;-1&quot;&gt;Demo ideas &lt;a class=&quot;direct-link&quot; href=&quot;#demo-ideas&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I really need to focus and get this product into the world. I&#39;m prone to distractions. One source of focus for me has been thinking about demos. When I consider what I will build with this product, it helps me understand what features are essential and what&#39;s cruft. Here are a list of demos I&#39;m itching to make. I think I&#39;ll live code them once I get a version of this product out there.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Card games&lt;/strong&gt;. I could build realtime, multiuser card games like blackjack.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple form submission&lt;/strong&gt;. One of the coolest applications of WebReducer is a form submission. You can point an HTML form at your endpoint and submit a form. With a &lt;code&gt;responder&lt;/code&gt; function, you can &lt;code&gt;301 redirect&lt;/code&gt; users back to your site or render an HTML thank you page. I love this demo because it&#39;s really really easy and practical.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive blog post&lt;/strong&gt;. In a blog post like this, there could be little buttons next to ideas like &lt;code&gt;(neat!)&lt;/code&gt; that users could click to tell me they dig something&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Personal dashboard&lt;/strong&gt;. This was kind of &lt;a href=&quot;https://twitter.com/_off_by_one/status/1457460794631892994&quot;&gt;the idea that got WebReducer going in the first place&lt;/a&gt;. I wanted a public URL in my email signature that would answer the question &amp;quot;how&#39;s it going...&amp;quot; I wanted to show my internet speed, sleep quality, etc. Recently I thought it&#39;d be cool to show % of uncalendared time, # of unread emails, etc. This is a very webhook-heavy application, it doesn&#39;t require a relational database, and is perfect for WebReducer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic consultant pricing&lt;/strong&gt;. I&#39;m not a consultant, so that makes this somewhat theoretical. But, if you wanted to book an hour of my time, perhaps there could be an algo aware of my calendar, inbox status, mood, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bloom filter&lt;/strong&gt;. I realized that I could write a hacky little bloom filter &lt;a href=&quot;https://dev.to/abhinpai/bloom-filter-in-javascript-1efe&quot;&gt;like this&lt;/a&gt;. This is obviously a weird use case, but it&#39;s just kind of cool that it&#39;s possible. There are probably cousins of this idea that are cooler.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any ideas for me? Want to be a beta tester? &lt;a href=&quot;https://webreducer.dev&quot;&gt;Give me your email&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Elevators, Subways, and Rocket Launchers: The Unpredictable Path of Software Development</title>
    <link href="/posts/elevators-subways-and-rockets/"/>
    <updated>2023-09-04T00:00:00Z</updated>
    <id>/posts/elevators-subways-and-rockets/</id>
    <content type="html">&lt;p&gt;There&#39;s been plenty of ink spilled on the topic of what to call software engineers. Hackers? Coders? Developers? This semantic debate has been mostly boring to me until recently. It&#39;s hard to find one term to use to describe our job because our job can change so much from day to day.&lt;/p&gt;
&lt;p&gt;When you&#39;re building at scale, the job can superficially resemble classical engineering. You need to build a model of the system based on empirical data, understand the constraints of the system, and build with enough flexibility to avoid a range of disasters.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/trainwreck-big.jpg&quot; alt=&quot;big train wreck&quot; /&gt;&lt;/p&gt;
&lt;p&gt;On the other hand, if you&#39;re building a new product that has no users, you use a profoundly different toolkit. The product itself is a hypothesis. You&#39;re trying to make something useful that doesn&#39;t yet exist. There&#39;s no reason to believe the thing your building will solve a real problem. You need to figure out how to build something as quickly as possible without contaminating the experiment. If you make it too cheaply the jankyness will get in the way and no one will use it, even if the idea is great. But, if you decide to use Kubernetes and micro-services with mature observability, you&#39;ll never stand anything up at all.&lt;/p&gt;
&lt;p&gt;I&#39;ve worked at startups and now I&#39;ve worked at a couple bigger companies for a few years, too. I&#39;ve been surprised to learn that you still need &amp;quot;hacking&amp;quot; at large organizations. Some projects–a prototype of an internal tool, for example–call for &amp;quot;hacking&amp;quot; while others that will be trafficked by millions of people need &amp;quot;engineering.&amp;quot;&lt;/p&gt;
&lt;p&gt;This one dimensional spectrum that I&#39;ve described with &amp;quot;hacking&amp;quot; on one side and &amp;quot;engineering&amp;quot; on the other is &lt;em&gt;still&lt;/em&gt; too simple.&lt;/p&gt;
&lt;p&gt;Some days I&#39;m a detective or a spy, trying to understand how someone exploited (or could exploit) a system. How do we know someone is who they say they are? Could anyone sneak past our security? How would we know that they did? What&#39;s the worst thing that might happen if they fool us?&lt;/p&gt;
&lt;p&gt;Other times I&#39;m wearing the hat of an ER doctor trying to understand the pathophysiology of the system in distress. &amp;quot;What could cause X reading to be elevated with Y symptom.... who should we page in for a consult? What can we do to stabilize the patient while we work on a diagnosis?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/grafana_prometheus.png&quot; alt=&quot;observability platforme example&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve occasionally worked on &amp;quot;core&amp;quot; projects where my goal is to support other engineers in what they build. When I&#39;m doing this I need to consider the emergent properties of a team of hundreds of people, slowly turning over over decades. People don&#39;t have perfect visibility or discipline. How can we create incentives for people to do the &amp;quot;right&amp;quot; thing? How can we make it as easy as possible to modernize legacy surfaces. On those days it feels like I&#39;m almost an urban planner, economist or even a politician.&lt;/p&gt;
&lt;p&gt;As software engineers, the things we build are unlike anything else out there. They&#39;re in constant flux and can change drastically over long periods of time. Imagine if the people building an elevator system in a skyscraper found out a decade later that they were actually building a subway system! That&#39;s what it sometimes feels like. The decisions that were made when you were making the elevator don&#39;t always hold up in hindsight. The context has shifted 90 degrees.&lt;/p&gt;
&lt;p&gt;And yet, you can&#39;t start over–a million people are using your subway every day. You just have to move forward with an eye toward the future. For all you know you might need to make this thing into a rocket launcher a decade from now.&lt;/p&gt;
&lt;p&gt;If I can leave you with one piece of advice... No one tells you when it&#39;s time to switch toolkits, and and it&#39;s necessary. If you miss the moment when it&#39;s time to change things up, your project will be in trouble. This job requires enormous flexibility. Ask yourself and your team, &amp;quot;what tools do we need for this project?&amp;quot; or &amp;quot;is it time to switch hats now that this thing is out in prod?&amp;quot; There&#39;s a lot to learn from different disciplines and from each other and we&#39;re still at the beginning.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Nextty: a radically collaborative computing environment</title>
    <link href="/posts/nextty-idea/"/>
    <updated>2023-11-27T00:00:00Z</updated>
    <id>/posts/nextty-idea/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;/img/nextty/logo.png&quot; alt=&quot;logo-idea&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I want to build a community computing platform. It&#39;s remote-friendly, but synchronous. It&#39;s orders of magnitude more immersive and cooperative than screen sharing. You could call it a chat app where you also write code... or a futuristic repl with your buddies. It&#39;s a computational space where you &lt;em&gt;solve problems&lt;/em&gt; together. As people start working in this kind of environment, the lines between programming and other communication blur. Everyone in the community ends up writing, or at least invoking, little programs all the time, even if they&#39;re not &amp;quot;software engineers.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/nextty/types-of-code.png&quot; alt=&quot;repl-chat&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There&#39;s a transition that&#39;s just begun in computing that makes this idea suddenly and increasingly viable. The &lt;a href=&quot;https://offbyone.us/posts/cost-of-computing/&quot;&gt;cost of expressing computational thought&lt;/a&gt; has begun an exponential decline (just like &lt;a href=&quot;https://ourworldindata.org/cheap-renewables-growth&quot;&gt;solar energy&lt;/a&gt;, &lt;a href=&quot;https://ourworldindata.org/grapher/the-price-for-lighting-per-million-lumen-hours-in-the-uk-in-british-pound?yScale=log&amp;amp;time=1301..latest&quot;&gt;light&lt;/a&gt;, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Moore%27s_law&quot;&gt;transistors&lt;/a&gt;) due to recent innovations in generative AI. The population of people capable of performing computational tasks is going to explode, but not in today&#39;s computing environments.&lt;/p&gt;
&lt;p&gt;One of the defining characteristics of communities is that they inhabit spaces. Communities can be made up of one or more spaces that are visually and functionally distinct and evolve over time. User interface elements can be built to represent common computational objects. Objects can be manipulated in multiple ways. Someone with more of a computing background might choose to manipulate an object in a lower-level way. But other users might manipulate the same object through a more traditional user interface.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/nextty/js-runtime.png&quot; alt=&quot;js-repl&quot; /&gt;&lt;/p&gt;
&lt;p&gt;No one&#39;s ever said &amp;quot;I love what you&#39;ve done with the place&amp;quot; about a slack channel or a zoom call. If you&#39;ve ever worked in a science lab with a healthy culture, you know that people &lt;em&gt;do&lt;/em&gt; say that about each others lab benches. People pin things on the walls that reflect their personality. People draw doodles on each others&#39; whiteboards while they&#39;re out at lunch. People  are social. People personalize the form and function of spaces if you let them. Remember MySpace?&lt;/p&gt;
&lt;p&gt;So what would a specific room, or channel look like? Well it would be a folder on a computer somewhere.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./example-channel
├── access.toml
├── agents
│   ├── decision-summarizer.md
│   └── visualization-helper.md
├── channel.sqlite
├── channel.toml
├── community.toml
├── profile.ts
├── readme.md
└── views
    ├── cover.html
    └── task-bar.html
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;access.toml&lt;/code&gt; would specify who can read/write/evaluate/search/mention/react/etc&lt;/li&gt;
&lt;li&gt;&lt;code&gt;agents&lt;/code&gt; would be a directory for LLM-backed bots. There would be some configuration specified in front-matter. You could also write non-LLM agents too. Communities can install plugins that make certain LLM providers or open source libraries available to every channel in the community so you don&#39;t need to set it up&lt;/li&gt;
&lt;li&gt;&lt;code&gt;profile.ts&lt;/code&gt; is a set of code that runs every time the channel node boots up. It&#39;s for configuring common commands (think &lt;code&gt;.bashprofile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;readme.md&lt;/code&gt; opens in the sidebar of the UI by default. It&#39;s a cover page to orient onlookers&lt;/li&gt;
&lt;li&gt;&lt;code&gt;channel.toml&lt;/code&gt; specifies some basic configuration and rules&lt;/li&gt;
&lt;li&gt;&lt;code&gt;community.toml&lt;/code&gt; is autogenerated and has details about how the channel connects to the community server.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;views&lt;/code&gt; directory contains html files (I think?). You can overwrite system partials and customize UI here. Very much TBD.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not every channel would need to be on the same hardware. Some communities would run all on one box, but in other cases, you might want to have a channel running on your own laptop. That way, when you&#39;re working in &amp;quot;your office&amp;quot; it&#39;s really fast. Plus, you can use it on  an airplane. The software itself isn&#39;t prescriptive about how this is done.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;If you&#39;re having trouble imagining this applying to a chat-repl, that&#39;s OK. It&#39;s hard to imagine. It&#39;s different from any computing environment you&#39;ve ever been in. It&#39;s social and vibrant. It&#39;s a place to gather, learn and teach. It&#39;s a breeze to pick up but extraordinarily deep and rich. You can get lost in it.&lt;/p&gt;
&lt;p&gt;OK, let&#39;s try a thought exercise. Out of all the digital communities you&#39;ve been a part of, what was the best one? Now imagine what that community would be capable of if instead of passing around links to various vendor-backed web apps on slack, people could exchange references to computational objects in the digital co-working space. There would be common abstractions for querying and visualizing data across all kinds of objects. There would also be batteries-included tools for what we&#39;d conventionally call &amp;quot;telemetry.&amp;quot; The entire computing environment would be highly observable and visible. The entire organization would share a common set of tools for seeing change over time.&lt;/p&gt;
&lt;p&gt;Everything would default to open. If you&#39;re working in a science lab, you can see what&#39;s on your coworkers desk. In this community, you can also see what&#39;s on your coworkers desk. In certain situations, that kind of openness doesn&#39;t make sense. Not every room has the same permission structure. There are locks and keys. Some people are read-only in some spaces. Some people can execute commands. Some people don&#39;t even know the room exists. But that&#39;s not the norm. The norm is open.&lt;/p&gt;
&lt;p&gt;For some communities, security and access control would be quite important. And while it&#39;s true there&#39;s a security burden on the community, today&#39;s conventionally run organizations have enormous security liabilities as well, they&#39;re just just somewhat obscured by vendors. Instead of your data being in one place where you can control it and monitor it, it&#39;s in who-knows how many organizations hands.&lt;/p&gt;
&lt;p&gt;While this type of computing is decidedly expensive, the software behind it would have to be free and open source. The community software platform offers tremendous value to organizations, and surely that value can be captured by people offering to help build these organizations. But, in order to make computational spaces available to all kinds of people and organizations, the foundational software would have to be free like water. It would have to be something that you could set up on a VM in 5 minutes and get started with. But it would have to have enough complexity to allow communities to grow and evolve over time into elaborate spaces that suit various needs.&lt;/p&gt;
&lt;p&gt;Note: this isn&#39;t a hyperscale idea. We don&#39;t need the software to scale gazillions of users because no community will be that big. Because this software is run by the organization, even if they pay someone to help, the software platform only needs to accommodate the largest types of communities. This smaller scale has implications about the platform. More will be done with flat files. If there&#39;s a need for a database, SQLite will get used and it&#39;s baked into the platform.&lt;/p&gt;
&lt;p&gt;Other note: if you&#39;re thinking &amp;quot;well this would never work at ___&amp;quot; you might be right. I can&#39;t imagine the security team at my day job ever saying yes to this. But not all computational communities look like your workplace. And I think over time as the software matures, it might support more and more types of organization and you might find yourself surprised who starts using it.&lt;/p&gt;
&lt;p&gt;So what&#39;s next? well... I think I need to start by getting an MVP together. Once I get some of the essential pieces in place, I hope to start developing &lt;em&gt;on the platform&lt;/em&gt;. If the code is on github, it&#39;ll be a side effect. To get a sense for how this feels, I&#39;m already using a chat app to jot down ideas and to see how things feel (it feels good!). If you want to get involved give me a shout out on the dead bird app. I&#39;m gonna migrate somewhere else soon, but haven&#39;t yet.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;inspiring-related-things&quot; tabindex=&quot;-1&quot;&gt;Inspiring related things &lt;a class=&quot;direct-link&quot; href=&quot;#inspiring-related-things&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Book: &lt;a href=&quot;https://www.goodreads.com/book/show/703532.Mindstorms?&quot;&gt;Mindstorms: Children, Computers, And Powerful Ideas by Seymour Papert&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;video-wrapper&quot;&gt;
  &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/-80VsIdAHZw?si=gzbXNsRnpQ9U00wH&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;</content>
  </entry>
  
  <entry>
    <title>Have a ball of mud? You need(ed) a map.</title>
    <link href="/posts/mud-map/"/>
    <updated>2024-02-18T00:00:00Z</updated>
    <id>/posts/mud-map/</id>
    <content type="html">&lt;p&gt;At work right now we’re currently working on modularizing our ~15 year old rails codebase. One conversation topic comes up frequently: is there ideal size range for a module? Should we make many small modules that are precise, or fewer course grained modules (or a mixture)? Should we have multiple tiers of nested modules?&lt;/p&gt;
&lt;p&gt;Maybe we should take some design inspiration from “the city”.&lt;/p&gt;
&lt;p&gt;Cities are big and complex! Despite this, people seem to get by pretty well in cities. We have tools to manage the level of complexity in our daily lives. Cities are continuously being constructed, just like software, by a team that never stops rolling over. The people who started it aren’t around anymore to say why every little thing is just the way it is. Cities are flawed and cities are beautiful.&lt;/p&gt;
&lt;p&gt;How do people wrangle the complexity of a city at a practical level? For one, we have maps. Millions of tourists visit New York City every year from all over the world. By and large, they manage to use the subway because of its &lt;a href=&quot;https://en.wikipedia.org/wiki/New_York_City_Subway_map&quot;&gt;incredible map&lt;/a&gt;. The map over-simplifies here and there, and makes some compromises. But, at its core, it achieves its purpose. It’s navigable by a tourist, with some effort.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/mud/nyc.jpg&quot; alt=&quot;New York City Subway Map&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If the people building your software are struggling to navigate when they onboard, or perform day to day tasks, chances are you need a map. There’s a bit of a catch-22, as well: if you’ve been building software without a map for 15 years, you haven’t had the tools to design well, so your first map might look like a ball of mud.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/mud/mud.webp&quot; alt=&quot;Big ball of mud&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This brings us back to the question at the top. When we’re refactoring a ball of mud, how big should we be chunking? Let’s look to the city.&lt;/p&gt;
&lt;p&gt;A single module should have the same level of complexity as a home or even apartment building would. It shouldn’t need a map to understand. Maybe there’s a blueprint from when it was built? But not a map for every day use. Even if a module has some design warts and compromises, you shouldn’t need a map to explain it.&lt;/p&gt;
&lt;p&gt;A set of modules representing an entire domain are sort of like a neighborhood. A neighborhood could have a map, especially for people who aren’t very familiar with the geography. But it shouldn’t be an essential tool for people who live and work there. They should be able to go off of memory for the most part.&lt;/p&gt;
&lt;p&gt;All big cities have maps. They wouldn’t function without them. Can you imagine what NYC would look like if millions of tourists descended &lt;em&gt;without&lt;/em&gt; maps?&lt;/p&gt;
&lt;p&gt;Codebases that don’t have maps over time tend to function like cities without maps. No one can agree were the borders of anything are. You can’t communicate your future plans relative to anything so people’s intentions are always colliding. If you try to turn to graph force diagrams, it just looks like a big ball of mud. I think that’s what we call a chronic condition… It’s manageable, it can improve, but it’s not really ever going to go away as a problem.&lt;/p&gt;
&lt;p&gt;Why can’t the map be the territory? What if our programming medium was inherently mappable. I’m not saying we’d write code in a visual medium, per se, or that we’ll be opening up our IDEs in VR. But I’m proposing that instead of first using the file system as a way to navigate code, we used some kind of purpose-built map.&lt;/p&gt;
&lt;p&gt;If the mapping tools were good enough, that engineers started to rely on it to make better decisions.&lt;/p&gt;
&lt;p&gt;Last week at work I was asked to spec out a greenfield project. I used &lt;a href=&quot;https://d2lang.com/&quot;&gt;D2Lang&lt;/a&gt;, as I’ve been known to do of late. I can’t show you the real diagram but it only looked slightly more complicated than this&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/mud/diagram.png&quot; alt=&quot;D2 Diagram&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In d2, you can produce this image with the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http: {
  users: {
    sign_up
  }

  tasks: {
    create
    assign
    complete
    defer
  }
}

# creates a user
http.users.sign_up -&amp;gt; users

# ...
http.tasks.* -&amp;gt; tasks

tasks: {
  shape: sql_table
  id: int {constraint: primary_key}
  last_updated: timestamp with time zone
  assignee_id: int {constraint: foreign_key}
  state: string
}

users: {
  shape: sql_table
  id: int {constraint: primary_key}
  name: timestamp with time zone
  email: int {constraint: foreign_key}
}

tasks -&amp;gt; users
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then an idea hit me. What if I had a program in my &lt;code&gt;$PATH&lt;/code&gt; that could run this as code.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ magic-cli ./prototype.d2 &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; deno&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It could clearly build a sqlite database from those sql tables. With some descriptive comments a garden variety gen-ai model could write some prototype code (and even some tests) for individual nodes.&lt;/p&gt;
&lt;p&gt;When you want to make big architectural changes, you don’t try to mutate the existing code into the new shape, you instead start over at the map to make a design decision about the intended change. Sure, a bunch of glue code will get wiped away with , and the new code will need to be extensively tested. But, because the map has been automatically updated with every manual change, you’re not starting frame square 1.&lt;/p&gt;
&lt;p&gt;Essentially, you’d “GPU compile” down this D2 + english comments + prompt into a nice little Deno binary with a little human in the loop to make sure it does what it’s supposed to.&lt;/p&gt;
&lt;p&gt;I am actually &lt;a href=&quot;https://github.com/terrastruct/d2/issues/1841&quot;&gt;interested in hacking on this&lt;/a&gt;, but I think its a long way off from anything you can use. But, if you’re interested in working on it with me, let me know!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>We need to be the historians of our own software</title>
    <link href="/posts/history/"/>
    <updated>2024-03-09T00:00:00Z</updated>
    <id>/posts/history/</id>
    <content type="html">&lt;p&gt;Codebases are peculiar things. They’re accumulators of debt and investments, warts and jewels. Over time, all the decisions–conscious and unconscious–made by a rotating cast of characters sum up to the current state of your main branch, &lt;code&gt;HEAD&lt;/code&gt;. The &lt;code&gt;HEAD&lt;/code&gt; may change every few minutes, hours or days. But as long as your codebase is alive, it’s changing.&lt;/p&gt;
&lt;p&gt;While every codebase is in motion, some things are immutable (or change so slowly they feel that way). Software can have character and a sense of place, &lt;a href=&quot;/posts/mud-map/&quot;&gt;much like a city&lt;/a&gt;. And just like a city, the bones of a codebase are built with imperfect context and are adapted over generations, with mixed results.&lt;/p&gt;
&lt;p&gt;When I close my eyes, the codebase I&#39;m working on almost has a smell to it. If I were to go away away for a few years and come back, this codebase would be different, but still itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/history/subway-construction.png&quot; alt=&quot;picture of people constructing subway&quot; /&gt;&lt;/p&gt;
&lt;small&gt;
&lt;a href=&quot;https://digitalcollections.nyhistory.org/islandora/object/nyhs%3A232506)&quot;&gt;nyhistory.org&lt;/a&gt;
&lt;/small&gt;
&lt;p&gt;When you make a change to your codebase, you’re not starting from zero. Sometimes it can feel like you’re standing on the shoulders of giants. Other times it feels like those who came before you made foolish choices. Either way, they defined the semantic water you’re swimming in, and they’re more like you than you might think: human beings trying to make something makes sense with imperfect tools and imperfect craft.&lt;/p&gt;
&lt;p&gt;If we want to influence the future of our software–to make it a better place to live and work in–we need to understand where it came from. We need to understand the human story of how we got from the first commit to &lt;code&gt;HEAD&lt;/code&gt;. We need history.&lt;/p&gt;
&lt;p&gt;When it comes to historical tools, we have a lot, actually. Git in particular comes to mind as an asset. Git is like an archival record of every little change over time. It can answer specific questions and contains many treasures if you take the time to look. For example, if you’re wondering how a file came to be, git has a lot to say, especially if there are commonly good PR descriptions in your project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/history/git.webp&quot; alt=&quot;illustration of git&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But it’s really hard to contextualize a long series of git commits. You can’t zoom out and understand the the world around the code. In 2012, what did the app feel like from the point of view of a user? What did the codebase feel like from the point of view of the engineers? Try checking out a historical commit and running it–If it’s even possible, it’s not fun or practical. Beyond the software itself, the human stories are totally out of reach. What was on the minds of the engineers? What were their struggles? Their triumphs?&lt;/p&gt;
&lt;p&gt;Depending on the age of your project, you may be able to speak with some people you see in the &lt;code&gt;git blame&lt;/code&gt;. You should. You should ask them questions and write down what they remember. It’s gold. But, that’s not enough. Gray matter is an imperfect storage medium. Memories fade. You’re not talking to the version of your colleague who just made a decisions, you’re talking to the person who has the benefit of hindsight. It’s not possible for them to reconstruct their cognitive state. Even if it were, you’d just be talking to one person. Most of the team has turned over.&lt;/p&gt;
&lt;h2 id=&quot;we-would-benefit-from-tools-we-don&#39;t-have&quot; tabindex=&quot;-1&quot;&gt;We would benefit from tools we don’t have &lt;a class=&quot;direct-link&quot; href=&quot;#we-would-benefit-from-tools-we-don&#39;t-have&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If we really want to understand the codebases we work in, we need better tools to construct the history. We need software to sift through git. I want to know how the quantities of certain types of code changed over time. I want to know when one style came into the codebase, plateaued and then declined. I want to understand trends.&lt;/p&gt;
&lt;p&gt;But beyond git, we need to understand the people. How did teams and team members change over time? What were they trying to do, in their own words? where did they think they were going? What was contentious? What was going on in the business? What was going on in the world?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/history/people.webp&quot; alt=&quot;illustration of fake historical figures programming&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There are many other artifacts that are not in git. Businesses always have a large set of documents in some kind of wiki or docs editor. They usually have author attribution and a big set of hyperlinks to other artifacts. This web is dense. The authors are very present-minded. They don’t include a lot for future readers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In the last 18 months, it’s gotten a lot easier to automatically summarize documents. We should be using Generative AI to make these artifacts easier to use.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I want a timeline view of a project’s history. I want to be able to pan and zoom. I want to go to different points in time and see product demos from the teams that worked on it. I want to have links to various documents with well picked excerpts. I want summaries. I want to understand quarter-by-quarter what the people were trying to achieve. I want to see when people joined and when they left.&lt;/p&gt;
&lt;img class=&quot;md:py-36 md:transform md:scale-150 md:-translate-x-1/8&quot; src=&quot;/img/history/timeline-product.png&quot; alt=&quot;product idea&quot; /&gt;
&lt;p&gt;Ultimately making history is an act of synthesis. What I’m picturing in my head isn’t the history itself, it’s a tool to make the history easier to distill. It would make a great deal of data about the past something that the whole team can understand and benefit from.&lt;/p&gt;
&lt;h2 id=&quot;history-can-provoke-hard-necessary-conversations&quot; tabindex=&quot;-1&quot;&gt;History can provoke hard, necessary conversations &lt;a class=&quot;direct-link&quot; href=&quot;#history-can-provoke-hard-necessary-conversations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Just like every other historical endeavor, constructing a narrative of the past can provoke difficult conversations. Why did we suddenly change all our engineering practices in 2015? Oh it’s because the whole team quit when the VPE was fired. Woah. Ouch. Why did we go all-in on &lt;code&gt;technology&lt;/code&gt;? Oh, it’s because &lt;code&gt;person&lt;/code&gt; (who may still work here or know people who does!) thought it was a great idea.&lt;/p&gt;
&lt;p&gt;In order to make this endeavor useful you need to adopt a nonjudgmental mindset. You should assume that the people who built this codebase were making the best decisions they could with the tools and information they had at the time. You may feel like better decisions are obvious, but you’re benefiting from information they didn’t have.&lt;/p&gt;
&lt;p&gt;And, sometimes these difficult conversations are necessary. Sometimes in order to move forward we have to be open about what really happened, and own it. History isn’t always rosy, and that’s OK.&lt;/p&gt;
&lt;h2 id=&quot;people-already-have-narratives-of-the-past-they&#39;re-just-not-always-backed-by-data&quot; tabindex=&quot;-1&quot;&gt;People already have narratives of the past, they&#39;re just not always backed by data &lt;a class=&quot;direct-link&quot; href=&quot;#people-already-have-narratives-of-the-past-they&#39;re-just-not-always-backed-by-data&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Humans make narratives like spiders make webs. Everyone in your organization has a story they tell themselves about the past. Someone might think &amp;quot;oh quality really dipped as we focused on growth pre-IPO&amp;quot; or &amp;quot;we really sped up when we got our new CTO&amp;quot; for example. But did these things really happen? These narratives reflect an individuals lived experience, and we shouldn&#39;t discount that. But can we observe anything empirical?&lt;/p&gt;
&lt;p&gt;It&#39;s absolutely possible to over-index on empirical data... and it&#39;s important to be mindful of that. But empirical data, when it is high quality, can help us zoom out and show a larger perspective than one individual on one team. It can mitigate fuzzy memories and group-think. It can show how parts of a system interacted over time. It can help us learn from the past, correct misconceptions, and make us overall better at whatever it is we&#39;re trying to do.&lt;/p&gt;
&lt;h2 id=&quot;you-can-make-it-easier-for-future-historians-(yourself)&quot; tabindex=&quot;-1&quot;&gt;You can make it easier for future historians (yourself?) &lt;a class=&quot;direct-link&quot; href=&quot;#you-can-make-it-easier-for-future-historians-(yourself)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Tomorrow&#39;s history is now. You an do things now that will make life easier for future historians. At work we have a repo called &lt;code&gt;artifacts&lt;/code&gt; which contains all sorts of runtime artifacts that would be difficult for us to reconstruct later without the app running. As an example, we have an artifact that gives us details about our ActiveRecord models. We know for each commit how many public instance methods existed any given model, or what AR associations and scopes were defined. We also know what Packwerk pack each model is associated with, and which team is the owner. For our Packwerk modules, we know each of their &amp;quot;modularity scores&amp;quot;–an in-house metric.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/history/artifacts.webp&quot; alt=&quot;artifacts&quot; /&gt;&lt;/p&gt;
&lt;p&gt;These artifacts are tracked in git and are updated in a &amp;quot;shadow repo&amp;quot; every time our monolith builds. This means we can see how they change over time. We&#39;ve had some of these artifacts for 8 months, and they&#39;ve proven &lt;em&gt;invaluable&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But we could be doing more. We could be conducting interviews. We could be journaling. We could be proactively making the lives of future historians easier, and we should.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Get shit done by warping friction-space</title>
    <link href="/posts/the-process/"/>
    <updated>2024-05-29T00:00:00Z</updated>
    <id>/posts/the-process/</id>
    <content type="html">&lt;p&gt;I have a process for making a codebase better over time, and it&#39;s so simple it could almost be a tweet:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make stuff that should be easy easy. Make stuff that should be hard hard.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If my brain had telemetry, I think the CPU load while at work would look something like this. Noisy and spiky.&lt;/p&gt;
&lt;div class=&quot;md:transform md:scale-110 md:-translate-x-1/8 sm:mt-24 sm:mb-12&quot;&gt;
    &lt;img class=&quot;border p-2 rounded shadow-sm&quot; src=&quot;/img/the-process/telemetry.png&quot; /&gt;
    &lt;img class=&quot;transform -translate-y-8 translate-x-1&quot; src=&quot;/img/the-process/friction footer.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;The spikes are part of what people call &amp;quot;friction&amp;quot; at work. Friction isn&#39;t &lt;em&gt;always&lt;/em&gt; bad, but usually it is. There are a handful of things that you actually want to require extra effort to do. But as a rule it&#39;s something we are constantly trying to minimize.&lt;/p&gt;
&lt;p&gt;My process starts by doing product engineering. Every time I pick something up, be it exciting or mundane, I take telemetry. When something seems really hard, I bookmark it.&lt;/p&gt;
&lt;p&gt;Periodically, I reflect on what&#39;s been hard. Here are some made up examples&lt;/p&gt;
&lt;div id=&quot;mermaid-container&quot; class=&quot;bg-white transform transition-all relative&quot;&gt;
&lt;div id=&quot;highlight&quot; class=&quot;absolute transform transitions-all rounded-lg z-75 opacity-0 bg-blue-300 z-50 border-8 border-blue-600&quot; style=&quot;width: 232px; height: 213px;&quot;&gt;&lt;/div&gt;
&lt;pre class=&quot;mermaid&quot;&gt;%%{init: {&amp;quot;themeVariables&amp;quot;: {&amp;quot;quadrant1TextFill&amp;quot;: &amp;quot;darkgreen&amp;quot;, &amp;quot;quadrant3TextFill&amp;quot;: &amp;quot;darkyellow&amp;quot;, &amp;quot;quadrant4TextFill&amp;quot;: &amp;quot;orange&amp;quot;, &amp;quot;quadrant2TextFill&amp;quot;: &amp;quot;red&amp;quot; } }}%%&amp;#10;quadrantChart&amp;#10;    title Friction Evaluation Matrix&amp;#10;    x-axis Was Easy --&amp;gt; Was Hard&amp;#10;    y-axis Should Have Been Easy --&amp;gt; Should Have Been Hard&amp;#10;    quadrant-1 Awesome work&amp;#10;    quadrant-2 Reckless zone&amp;#10;    quadrant-3 Necessary toil&amp;#10;    quadrant-4 Frustrating bullshit&amp;#10;&amp;#10;    Necessary refactoring: [0.45, 0.17]&amp;#10;    Writing tests: [0.1, 0.13]&amp;#10;    Adding a module: [0.7, 0.23]&amp;#10;    Adding a microservice: [0.88, 0.33]&amp;#10;&amp;#10;    Architecture project: [0.87, 0.79]&amp;#10;    Building consensus: [0.7, 0.59]&amp;#10;    Adopting new technology: [0.6, 0.69]&amp;#10;    Mentorship: [0.62, 0.8]&amp;#10;&amp;#10;&amp;#10;    Add another ORM Relation: [0.20, 0.64]&amp;#10;    SQL against prod: [0.15, 0.72]&amp;#10;&lt;/pre&gt;
&lt;div class=&quot;bg-red-100 border-2 border-red-400 rounded-lg text-red-700 px-3 pb-0 m-3 flex items-start gap-4&quot; role=&quot;alert&quot;&gt;
  &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 24 24&quot; stroke-width=&quot;1.5&quot; stroke=&quot;currentColor&quot; class=&quot;w-6 h-6 transform translate-y-3.5&quot;&gt;
    &lt;path stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; d=&quot;M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z&quot;&gt;&lt;/path&gt;
  &lt;/svg&gt;
  &lt;div class=&quot;text-sm&quot;&gt;
    &lt;p class=&quot;font-bold&quot;&gt;Both axes are subjective&lt;/p&gt;
  &lt;p&gt;But, the X-Axis is a qualitative measurement of your experience–what was and was not hard. The Y-Axis is &lt;span class=&quot;italic&quot;&gt;more&lt;/span&gt; subjective. It&#39;s your opinion about what should be.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Later, when I&#39;m doing self-driven work, I try to react to my observations.&lt;/p&gt;
&lt;h2 id=&quot;address-the-reckless-zone&quot; tabindex=&quot;-1&quot;&gt;Address The Reckless Zone &lt;a class=&quot;direct-link&quot; href=&quot;#address-the-reckless-zone&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If I observe something in The Reckless Zone, it&#39;s important to address quickly. For example, if engineers at a small but growing startup are running queries against the main production DB, that&#39;s something that needs to be addressed because it could take down the system.&lt;/p&gt;
&lt;p&gt;To mitigate observations in The Reckless Zone, we need to &lt;em&gt;add&lt;/em&gt; friction. It&#39;s important that the friction is automated. I&#39;m talking about linters, PR bots, Slack alerts, etc. We should minimize the extent to which someone needs to &lt;em&gt;enforce&lt;/em&gt; stuff.&lt;/p&gt;
&lt;h2 id=&quot;celebrate-awesome-work&quot; tabindex=&quot;-1&quot;&gt;Celebrate awesome work &lt;a class=&quot;direct-link&quot; href=&quot;#celebrate-awesome-work&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s important for ICs&#39; work to be &lt;em&gt;visible&lt;/em&gt; and celebrated. When folks do good, hard things, the broader team needs to know. Different companies have different mechanisms of this, but usually there&#39;s at least a slack channel for celebrating wins. If not, maybe there should be.&lt;/p&gt;
&lt;h2 id=&quot;work-on-frustrating-bullshit&quot; tabindex=&quot;-1&quot;&gt;Work on Frustrating Bullshit &lt;a class=&quot;direct-link&quot; href=&quot;#work-on-frustrating-bullshit&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whenever you encounter something that is hard but should be easy, it&#39;s important to take a careful look at it. This could be a golden opportunity to improve the broader tech org.&lt;/p&gt;
&lt;p&gt;Not everything is easily addressable. Some problems are not technical ones. But this is the quadrant that&#39;s holding you back. You need to build a culture that chips away at this unnecessary friction over time, and periodically makes investments that produce big leaps forward.&lt;/p&gt;
&lt;p&gt;When teams are &amp;quot;too busy&amp;quot; to make investments in working better, that means they&#39;ll be just as &amp;quot;busy&amp;quot; next year. They probably feel chronically overworked and frustrated. Imagining shoveling a huge pile of dirt with a spoon and thinking &amp;quot;there&#39;s gotta be a better way,&amp;quot; only to be told &amp;quot;nope, don&#39;t think about that! too much dirt!&amp;quot;&lt;/p&gt;
&lt;h2 id=&quot;the-org-is-a-dynamic-system&quot; tabindex=&quot;-1&quot;&gt;The org is a dynamic system &lt;a class=&quot;direct-link&quot; href=&quot;#the-org-is-a-dynamic-system&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Different people in the organization will have different perspectives on what needs to be hard and what doesn&#39;t. It&#39;s a big dynamic system. It&#39;s hard work to build enough consensus, but it&#39;s worth it. When you achieve a well-calibrated friction space, goals are more likely to get met, people are happier, and the company starts to trust that the engineering organization can do what it says.&lt;/p&gt;
&lt;div class=&quot;bg-gray-800 rounded px-6 shadow-sm md:transform md:scale-150 md:-translate-x-1/8 sm:mt-48 sm:mb-72&quot;&gt;
    &lt;img class=&quot;p-4&quot; src=&quot;/img/the-process/dynamics.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;Let&#39;s take a minute to zoom in on a subgraph here...&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;graph BT&amp;#10;    credibility[&amp;quot;Credibility&amp;quot;] --&amp;gt;|++| tech_investments[&amp;quot;Tech Investments&amp;quot;]&amp;#10;    linkStyle 0 stroke:green,stroke-width:3px&amp;#10;    tech_investments -.-&amp;gt;|----| friction[Friction]&amp;#10;    linkStyle 1 stroke:#f22,stroke-width:7px&amp;#10;    tech_investments -.-&amp;gt;|++| friction&amp;#10;    linkStyle 2 stroke:green,stroke-width:3px&amp;#10;    tech_investments --&amp;gt;|++| reliability[&amp;quot;Reliability&amp;quot;]&amp;#10;    linkStyle 3 stroke:green,stroke-width:4px&amp;#10;    tech_investments --&amp;gt;|++| security[&amp;quot;Security&amp;quot;]&amp;#10;    linkStyle 4 stroke:green,stroke-width:2px&amp;#10;    tech_investments --&amp;gt;|--| capacity[&amp;quot;Capacity (gas)&amp;quot;]&amp;#10;    linkStyle 5 stroke:red,stroke-width:2px&amp;#10;    tech_investments --&amp;gt;|+++| cohesion[&amp;quot;Conative Cohesion&amp;quot;]&amp;#10;    linkStyle 6 stroke:green,stroke-width:4px&amp;#10;    tech_investments --&amp;gt;|-| surface_area[&amp;quot;Surface Area&amp;quot;]&amp;#10;    linkStyle 7 stroke:red,stroke-width:1px&amp;#10;    tech_investments --&amp;gt;|++| mpg[&amp;quot;Milage (mpg)&amp;quot;]&amp;#10;    linkStyle 8 stroke:green,stroke-width:2px&amp;#10;    tech_investments --&amp;gt;|++| documentation[&amp;quot;Documentation&amp;quot;]&amp;#10;    linkStyle 9 stroke:green,stroke-width:2px&amp;#10;&amp;#10;    capacity --&amp;gt;|++| tech_investments&amp;#10;    linkStyle 10 stroke:green,stroke-width:2px&amp;#10;&amp;#10;    documentation --&amp;gt;|+| mpg&amp;#10;    linkStyle 11 stroke:green,stroke-width:2px&amp;#10;&amp;#10;    reliability --&amp;gt;|++| credibility&amp;#10;    linkStyle 12 stroke:green,stroke-width:2px&amp;#10;&amp;#10;    mpg --&amp;gt;|++| credibility&amp;#10;    linkStyle 13 stroke:green,stroke-width:2px&amp;#10;&amp;#10;    security --&amp;gt;|++| credibility&amp;#10;    linkStyle 14 stroke:green,stroke-width:2px&amp;#10;&amp;#10;    friction --&amp;gt;|----| mpg&amp;#10;    linkStyle 15 stroke:red,stroke-width:5px&amp;#10;&amp;#10;&lt;/pre&gt;
&lt;h3 id=&quot;tech-investments&quot; tabindex=&quot;-1&quot;&gt;Tech Investments &lt;a class=&quot;direct-link&quot; href=&quot;#tech-investments&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tech investments take away capacity you could spend elsewhere. These investments add &lt;em&gt;and&lt;/em&gt; remove friction, but they take away more than they add. They enhance security, reliability, and conative cohesion. Tech investments (in contrast to product investments), tend to modestly remove surface area (a liability). Tech investments empty your gas tank, but increase your milage.&lt;/p&gt;
&lt;p&gt;In order to do tech investments, you need some credibility in the org, but by doing tech investments well, you tend to build credibility, as well.&lt;/p&gt;
&lt;h3 id=&quot;cognitive-cohesion&quot; tabindex=&quot;-1&quot;&gt;Cognitive cohesion &lt;a class=&quot;direct-link&quot; href=&quot;#cognitive-cohesion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Conative cohesion is a mostly squishy idea that things that belong together are found together. At work, we try to measure this by seeing how many modules need to be modified in a single chunk of work. If for most tasks you can open up a single module, load it into your brain, and ignore the rest of the application, that&#39;s a proxy for high cognitive cohesion.&lt;/p&gt;
&lt;p&gt;Another way to get a feel for cognitive cohesion: how many people have to be involved to make a seemingly simple change happen? Is it a couple people on a team, or 4 teams coordinating via weekly &amp;quot;scrum of scrum&amp;quot; meetings? At prior company, creating a new promotional discount took a quarter and required constant syncing of more teams than I&#39;d care to admit. That was because the details of discounts were spread out into a web of tiny, brittle micro-services that had &lt;strong&gt;low cognitive cohesion&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;graph BT&amp;#10;    tech_investments[Tech Investments] --&amp;gt;|+++| cohesion[&amp;quot;Conative Cohesion&amp;quot;]&amp;#10;    linkStyle 0 stroke:green, stroke-width:4px&amp;#10;    cohesion --&amp;gt;|--| friction&amp;#10;    linkStyle 1 stroke:red, stroke-width:3px&amp;#10;    cohesion --&amp;gt;|+| morale&amp;#10;    linkStyle 2 stroke:green, stroke-width:1px&amp;#10;    cohesion --&amp;gt;|++++| code_reads[&amp;quot;Code Read MPG&amp;quot;]&amp;#10;    linkStyle 3 stroke:green, stroke-width:5px&amp;#10;    cohesion --&amp;gt;|++| code_writes[&amp;quot;Code Writes MPG&amp;quot;]&amp;#10;    linkStyle 4 stroke:green, stroke-width:2px&amp;#10;    documentation --&amp;gt;|+| cohesion&amp;#10;    linkStyle 5 stroke:green, stroke-width:1px&amp;#10;&lt;/pre&gt;
&lt;h2 id=&quot;velocity&quot; tabindex=&quot;-1&quot;&gt;Velocity &lt;a class=&quot;direct-link&quot; href=&quot;#velocity&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s tempting to think of product investment and tech investment as zero-sum. An hour spent on one is an hour not spent on the other. But friction (in the wrong places) has a profound drag on product velocity.&lt;/p&gt;
&lt;p&gt;On the other side of the coin, it&#39;s not really possible for engineers to achieve business outcomes with tech investments alone.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;graph BT&amp;#10;    friction --&amp;gt;|---| velocity[&amp;quot;Product Investment (m/s^2)&amp;quot;]&amp;#10;    linkStyle 0 stroke:#f22, stroke-width:3px&amp;#10;    velocity --&amp;gt;|+++| outcomes&amp;#10;    linkStyle 1 stroke:green, stroke-width:5px&amp;#10;    capacity --&amp;gt;|++| velocity&amp;#10;    linkStyle 2 stroke:green, stroke-width:3px&amp;#10;    reliability --&amp;gt;|+| velocity&amp;#10;    linkStyle 3 stroke:green, stroke-width:1px&amp;#10;&lt;/pre&gt;
&lt;h2 id=&quot;changing-friction-space&quot; tabindex=&quot;-1&quot;&gt;Changing friction space &lt;a class=&quot;direct-link&quot; href=&quot;#changing-friction-space&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The real task of senior-level ICs is to continuously (and strategically) mold the friction space in response to the needs of the business. The reason why this is continuous is because the needs of the business are in constant flux. A well-calibrated friction space for a scrappy 10 person engineering organization will be totally dysfunctional for a 20 or 200 person org.&lt;/p&gt;
&lt;p&gt;I&#39;m not a big fan of dedicated teams that are charged with reducing friction. It can be the right move, but these teams tend to lose touch with the reality of the friction space and work off of their mental model of the friction space which drifts away from reality. Instead I&#39;m in favor of empowering every engineer to work on the friction space. If there is a dedicated team, it&#39;s important for them to have good feedback mechanisms. This could take the shape of pair programming with &amp;quot;product engineering&amp;quot; or rotating onto teams for a quarter per year.&lt;/p&gt;
&lt;img class=&quot;rounded-lg shadow border&quot; src=&quot;/img/the-process/friction-space.webp&quot; /&gt;
&lt;p&gt;If you&#39;re company hasn&#39;t learned how to manage it&#39;s friction space yet, work on it. Things will only get more painful until you figure out how to manage friction as part of development.&lt;/p&gt;
&lt;script&gt;

function isDesktop() {
    const viewportWidth = window.innerWidth;
    return viewportWidth &gt; 1024;
}

const matrix = {
  stickyClasses: [&#39;sticky&#39;, &#39;border&#39;, &#39;shadow-lg&#39;, &#39;rounded-xl&#39;, &#39;border&#39;, &#39;top-0&#39;, &#39;scale-50&#39;, &#39;-translate-y-24&#39;, &#39;translate-x-full&#39;, &#39;mr-36&#39;, &#39;rotate-1&#39;, &#39;skew-1&#39;,

  
  ],
  
  isStuck() {
    return document.querySelector(&#39;#mermaid-container&#39;).classList.contains(&#39;sticky&#39;)
  },
  
  stick() {
    document.querySelector(&#39;#mermaid-container&#39;).classList.add(...matrix.stickyClasses)
  },
  
  unstick() {
    document.querySelector(&#39;#mermaid-container&#39;).classList.remove(...matrix.stickyClasses)
    matrix.removeHighlight();
  },

  hide() {
    document.querySelector(&#39;#mermaid-container&#39;).classList.add(&quot;opacity-0&quot;)
  },

  show() {
    document.querySelector(&#39;#mermaid-container&#39;).classList.remove(&quot;opacity-0&quot;)
  },

  getQuadrantCoordinates(index) {
    const container = document.querySelector(&#39;#mermaid-container&#39;);
    const quad = document.querySelectorAll(&#39;#mermaid-container .quadrant&#39;)[index]

    const parentRect = container.getBoundingClientRect();
    const elementRect = quad.getBoundingClientRect();

    return {
        top: elementRect.top - parentRect.top - 3,
        right: parentRect.right - elementRect.right,
        bottom: parentRect.bottom - elementRect.bottom,
        left: elementRect.left - parentRect.left - 1
    };
    return delta
  },

  highlight(index) {
    if (!matrix.isStuck()) { return; }
    matrix.highlighted = index;
    const coords = matrix.getQuadrantCoordinates(index);
    const highlight = document.querySelector(&#39;#mermaid-container #highlight&#39;);
    highlight.classList.remove(&#39;opacity-0&#39;);
    highlight.classList.add(&#39;opacity-25&#39;);

    highlight.style.transform = `translate(${coords.left * 2}px, ${coords.top * 2}px)`;
  },

  removeHighlight() {
    matrix.highlighted = null;
    const highlight = document.querySelector(&#39;#mermaid-container #highlight&#39;);
    highlight.classList.add(&#39;opacity-0&#39;);
    highlight.classList.remove(&#39;opacity-25&#39;);
  },

  currentHighlight() {
    return matrix.highlighted;
  },
}

function throttle(func, limit) {
  let inThrottle;
  return function() {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() =&gt; inThrottle = false, limit);
    }
  };
}

// Debounce function to limit the rate at which a function is executed
function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() =&gt; func.apply(context, args), wait);
  };
}

function trackScrollingFor(selectors, callback) {
  const elements = selectors.map(selector =&gt; ({
    selector: selector,
    element: document.querySelector(selector)
  }));
  let lastScrollY = window.scrollY;
  let direction = &#39;none&#39;;

  const getScrollPositions = () =&gt; {
    const positions = {};
    elements.forEach(({selector, element}) =&gt; {
      if (element) {
        const rect = element.getBoundingClientRect();
        positions[selector] = rect.top;
      } else {
        console.warn(`Element for selector &quot;${selector}&quot; not found.`);
      }
    });
    return positions;
  };

  const handleScroll = throttle(() =&gt; {
    const currentScrollY = window.scrollY;
    if (currentScrollY &gt; lastScrollY) {
      direction = &#39;down&#39;;
    } else if (currentScrollY &lt; lastScrollY) {
      direction = &#39;up&#39;;
    } else {
      direction = &#39;none&#39;;
    }
    lastScrollY = currentScrollY;

    const positions = getScrollPositions();
    callback(positions, direction);
  }, 100);

  window.addEventListener(&#39;scroll&#39;, handleScroll);

  // Initial call to provide positions at load time
  handleScroll();
}

const selectors = [
  &#39;.mermaid&#39;,
  &#39;#the-org-is-a-dynamic-system&#39;,
  &#39;#address-the-reckless-zone&#39;,
  &#39;#celebrate-awesome-work&#39;,
  &#39;#work-on-frustrating-bullshit&#39;
];

trackScrollingFor(selectors, (positions, direction) =&gt; {

  if (!isDesktop()) {
    return;
  }

  if (positions[&#39;.mermaid&#39;] &lt; 15 &amp;&amp; direction === &#39;down&#39;) {
    matrix.stick();
  }

  if (positions[&#39;#the-org-is-a-dynamic-system&#39;] &lt; window.innerHeight * 0.33 &amp;&amp; direction === &#39;down&#39;) {
    matrix.hide();
  }

  if (positions[&#39;#the-org-is-a-dynamic-system&#39;] &gt; window.innerHeight * 0.33 &amp;&amp; direction === &#39;up&#39;) {
    matrix.show();
  }

  if (positions[&#39;#address-the-reckless-zone&#39;] &gt; window.innerHeight / 2 &amp;&amp; matrix.isStuck() &amp;&amp; direction === &#39;up&#39;) {
    matrix.unstick();
  }

  if (positions[&#39;#address-the-reckless-zone&#39;] &lt; 220 &amp;&amp; direction === &#39;down&#39;) {
    matrix.highlight(1)
  }

  if (positions[&#39;#celebrate-awesome-work&#39;] &lt; 220 &amp;&amp; direction === &#39;down&#39;) {
    matrix.highlight(0)
  }

  if (positions[&#39;#work-on-frustrating-bullshit&#39;] &lt; 220 &amp;&amp; direction === &#39;down&#39;) {
    matrix.highlight(3)
  }




  if (matrix.currentHighlight() === 0 &amp;&amp; positions[&#39;#address-the-reckless-zone&#39;] &gt; -100 &amp;&amp; direction === &#39;up&#39;) {
    matrix.highlight(1)
  }

  if (matrix.currentHighlight() === 3 &amp;&amp; positions[&#39;#celebrate-awesome-work&#39;] &gt; -100 &amp;&amp; direction === &#39;up&#39;) {
    matrix.highlight(0)
  }


});







// Function to run when the element is about to scroll out of view at the top
// function handleIntersection(entries) {
//     entries.forEach(entry =&gt; {
//         if (!entry.isIntersecting &amp;&amp; entry.boundingClientRect.top &lt; 0) {
//             document.querySelector(&#39;#mermaid-container&#39;).classList.add(&#39;sticky&#39;, &#39;border&#39;, &#39;shadow-lg&#39;, &#39;rounded-xl&#39;, &#39;border&#39;, &#39;top-0&#39;, &#39;scale-50&#39;, &#39;-translate-y-24&#39;, &#39;-translate-x-full&#39;, &#39;ml-24&#39;, &#39;-rotate-1&#39;, &#39;skew-1&#39;)
//         }
//     });
// }

// // Options for the Intersection Observer
// let options = {
//     root: null, // observing intersections relative to the viewport
//     rootMargin: &#39;0px&#39;,
//     threshold: 1
// };

// // Creating an observer with the function and options
// let observer = new IntersectionObserver(handleIntersection, options);

// // Target element to observe
// let targetElement = document.querySelector(&#39;.mermaid&#39;);

// // Start observing the target
// observer.observe(targetElement);
&lt;/script&gt;
</content>
  </entry>
  
  <entry>
    <title>Modeling a big spaghetti app</title>
    <link href="/posts/model-spaghetti/"/>
    <updated>2024-07-07T00:00:00Z</updated>
    <id>/posts/model-spaghetti/</id>
    <content type="html">&lt;h2 id=&quot;the-problem&quot; tabindex=&quot;-1&quot;&gt;The problem &lt;a class=&quot;direct-link&quot; href=&quot;#the-problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At work, we&#39;ve been modularizing our old Rails monolith for the better part of 2 years. We&#39;ve made a &lt;em&gt;ton&lt;/em&gt; of progress and I&#39;ve personally learned a lot. Unfortunately, it&#39;s been challenging to share details publicly because all the examples are closed source, and sensitive. If I wanted to write any open-source any visualization tooling, it&#39;d be hard to test on anything resembling a real project.&lt;/p&gt;
&lt;p&gt;I recently made some progress in this problem space. LLMs are great at synthetic data generation, and it turns out that Claude 3.5 Sonnet can, given the right combination of prompts, produce a pretty mean looking spaghetti ball monolith.&lt;/p&gt;
&lt;p&gt;First, I asked Claude to generate me a ball of mud model layer.&lt;/p&gt;
&lt;img class=&quot;md:py-36 md:transform md:scale-150 md:-translate-x-1/8&quot; src=&quot;/img/model-spaghetti/01.svg&quot; alt=&quot;initial synthetic noodles&quot; /&gt;
&lt;p&gt;I didn&#39;t feel like this was &amp;quot;monolith-y enough&amp;quot; so I coaxed it to get more tangled. I also asked for it to provide edge descriptions and a few different user types.&lt;/p&gt;
&lt;img class=&quot;md:pt-48 mb-36 md:transform md:scale-175 md:-translate-x-1/8&quot; src=&quot;/img/model-spaghetti/02.svg&quot; alt=&quot;second synthetic noodles&quot; /&gt;
&lt;p&gt;That&#39;s what I&#39;m talking about.&lt;/p&gt;
&lt;p&gt;Then, in a new session, I asked if Claude could generate a graphql schema for this app. It did a good job on it&#39;s first attempt. I asked it in a subsequent request to add documentation to the schema. I also needed to tell it to use relay... I ended up with this:&lt;/p&gt;
&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Root Query type&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Provides entry points for reading data from the system&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Retrieves a user by their unique identifier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Fetches a product by its unique identifier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Retrieves an order by its unique identifier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Fetches a category by its unique identifier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Retrieves a support ticket by its unique identifier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;supportTicket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SupportTicket&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Fetches a warehouse by its unique identifier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;warehouse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Warehouse&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Retrieves a paginated list of products with optional filtering&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;ProductFilterInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Fetches a paginated list of orders with optional filtering&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;orders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;OrderFilterInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Root Mutation type&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Provides entry points for modifying data in the system&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mutation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Creates a new order&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;CreateOrderInput&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Updates an existing product&#39;s information&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;updateProduct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;UpdateProductInput&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Creates a new support ticket&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createSupportTicket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;CreateSupportTicketInput&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SupportTicket&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Updates the inventory quantity for a product&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;updateInventory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Inventory&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a user in the system&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Profile&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of user&#39;s addresses&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AddressConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of user&#39;s orders&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;orders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of user&#39;s reviews&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;reviews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReviewConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;wishlist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Wishlist&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;shoppingCart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShoppingCart&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of user&#39;s notifications&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of user&#39;s payment methods&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;paymentMethods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethodConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of user&#39;s membership plans&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;membershipPlans&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MembershipPlanConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Role&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;loyaltyPoints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a product in the system&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;inventory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Inventory&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of product reviews&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;reviews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReviewConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of product images&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;images&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of product attributes&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AttributeConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of related products&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;relatedProducts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;supplier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Supplier&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents an order in the system&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of order items&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;orderItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderItemConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;totalAmount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderStatus&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;shippingAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;billingAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of payment transactions for this order&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;paymentTransactions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentTransactionConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of shipments for this order&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;shipments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShipmentConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a product category&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;parentCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of subcategories&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;subcategories&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CategoryConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of products in this category&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the inventory of a product in a specific warehouse&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Inventory&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;warehouse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Warehouse&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of stock movements for this inventory&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;stockMovements&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StockMovementConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a warehouse in the system&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Warehouse&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of inventories in this warehouse&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;inventories&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InventoryConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;manager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a customer support ticket&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SupportTicket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;customer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TicketStatus&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of responses to this ticket&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TicketResponseConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;updatedAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a product supplier&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Supplier&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of products supplied by this supplier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;qualityRating&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a product review&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Review&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a payment transaction&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentTransaction&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentStatus&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;paymentMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a shipment for an order&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Shipment&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;trackingNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShipmentStatus&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;estimatedDelivery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a notification sent to a user&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Notification&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a user&#39;s profile information&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Profile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;phoneNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;dateOfBirth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents an address&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;street&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;zipCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a user&#39;s wishlist&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Wishlist&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of products in the wishlist&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a user&#39;s shopping cart&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShoppingCart&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# Paginated list of items in the cart&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CartItemConnection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;totalAmount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a payment method&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethod&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethodType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;lastFourDigits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;expirationDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a user&#39;s membership plan&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MembershipPlan&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MembershipPlanType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;startDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;endDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a user role with associated permissions&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Role&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Permission&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a single permission&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Permission&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents an image, typically associated with a product&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;altText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a product attribute&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Attribute&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents an item in an order&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderItem&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a stock movement (inbound, outbound, or adjustment)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StockMovement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;inventory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Inventory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StockMovementType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a response to a support ticket&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TicketResponse&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;ticket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SupportTicket&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;responder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;createdAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents an item in a shopping cart&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CartItem&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents a type of membership plan&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MembershipPlanType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;features&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Connection Types&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# These types enable pagination for lists of objects&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AddressConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AddressEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AddressEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;OrderEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReviewConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ReviewEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReviewEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Review&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NotificationEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethodConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PaymentMethodEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethodEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MembershipPlanConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MembershipPlanEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MembershipPlanEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MembershipPlan&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProductEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProductEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ImageEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AttributeConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AttributeEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AttributeEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderItemConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;OrderItemEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderItemEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderItem&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentTransactionConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PaymentTransactionEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentTransactionEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentTransaction&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShipmentConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ShipmentEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShipmentEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Shipment&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CategoryConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CategoryEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CategoryEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StockMovementConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StockMovementEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StockMovementEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StockMovement&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InventoryConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InventoryEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InventoryEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Inventory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TicketResponseConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TicketResponseEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TicketResponseEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TicketResponse&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CartItemConnection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CartItemEdge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;pageInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CartItemEdge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CartItem&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents pagination information for connections&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PageInfo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;hasNextPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;hasPreviousPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;startCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;endCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Enums&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the possible statuses of an order&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderStatus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;PENDING&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;PROCESSING&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;SHIPPED&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;DELIVERED&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;CANCELLED&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the possible statuses of a support ticket&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TicketStatus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;OPEN&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;IN_PROGRESS&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;RESOLVED&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;CLOSED&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the possible statuses of a payment transaction&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentStatus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;PENDING&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;COMPLETED&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;FAILED&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;REFUNDED&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the possible statuses of a shipment&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShipmentStatus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;PROCESSING&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;SHIPPED&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;IN_TRANSIT&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;DELIVERED&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the types of notifications that can be sent to users&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;ORDER_UPDATE&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;PRICE_DROP&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;BACK_IN_STOCK&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;PROMOTIONAL&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the types of payment methods available&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentMethodType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;CREDIT_CARD&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;DEBIT_CARD&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;PAYPAL&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;BANK_TRANSFER&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Represents the types of stock movements&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StockMovementType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;INBOUND&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;OUTBOUND&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;ADJUSTMENT&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Input types for mutations&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Input for creating a new order&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;CreateOrderInput&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;orderItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token atom-input class-name&quot;&gt;OrderItemInput&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;shippingAddressId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;billingAddressId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;paymentMethodId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Input for specifying order items when creating an order&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;OrderItemInput&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;productId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Input for updating a product&#39;s information&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;UpdateProductInput&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;categoryId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Input for creating a new support ticket&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;CreateSupportTicketInput&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;customerId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Input for filtering products in queries&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;ProductFilterInput&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;categoryId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;minPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;maxPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;inStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Boolean&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Input for filtering orders in queries&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;OrderFilterInput&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderStatus&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;minTotalAmount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;maxTotalAmount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;Float&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;fromDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;toDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Custom scalar for handling dates and times&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;scalar&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First of all, this schema &lt;em&gt;works&lt;/em&gt;. If you want to play with it, you can. You can go to &lt;a href=&quot;https://gorgeous-lace-agenda.glitch.me/graphql&quot;&gt;this glitch server&lt;/a&gt; I hooked up that will actually run. If you send GraphQL requests to this endpoint, you&#39;ll get responses... and if you navigate there in your browser, you can play with the schema.&lt;/p&gt;
&lt;img class=&quot;md:py-36 md:transform md:scale-150 md:-translate-x-1/8&quot; src=&quot;/img/model-spaghetti/graphql-server.png&quot; alt=&quot;apollo server&quot; /&gt;
&lt;p&gt;Next, I wanted to see if it could generate rails code in a D2 diagram. It took some coaxing, but I asked it to generate D2 nodes with ActiveRecord source code in them. Here&#39;s the model graph.&lt;/p&gt;
&lt;img class=&quot;md:pt-40 md:pb-40 md:transform md:scale-175 md:-translate-x-1/8&quot; src=&quot;/img/model-spaghetti/models-with-src.svg&quot; alt=&quot;Active record soup&quot; /&gt;
&lt;p class=&quot;md:py-12&quot;&gt;OK, what about the GraphQL layer?&lt;/p&gt;
&lt;img class=&quot;md:pt-36 md:pb-36 md:transform md:scale-175 md:-translate-x-1/8&quot; src=&quot;/img/model-spaghetti/graphql-with-src.svg&quot; alt=&quot;GraphQL soup&quot; /&gt;
&lt;p&gt;Next, I asked Claude to imagine what a service layer might look like... I had a hard time getting Claude to do &amp;quot;bad&amp;quot; software engineering, so the service layer actually looks pretty reasonable to me.&lt;/p&gt;
&lt;img class=&quot;md:pt-40 md:pb-40 md:transform md:scale-175 md:-translate-x-1/8&quot; src=&quot;/img/model-spaghetti/services.svg&quot; alt=&quot;Service layer&quot; /&gt;
&lt;p&gt;Want to see what the whole monstrosity looks like?&lt;/p&gt;
&lt;img class=&quot;md:pt-40 md:pb-36 md:transform md:scale-175 md:-translate-x-1/8&quot; src=&quot;/img/model-spaghetti/everything.svg&quot; alt=&quot;Spaghetti mud&quot; /&gt;
&lt;p&gt;Yep... that&#39;s a big ball of mud right there. That&#39;s a plate of Spaghetti. That&#39;s something that needs refactoring.&lt;/p&gt;
&lt;p&gt;So why is this so cool? Beyond the fact that Claude&#39;s abilities are neat, it demonstrates that with an LLM we can make realistic, runnable synthetic code. Next, I&#39;m going to try to use this code as a sort of lab for studying legacy enterprise codebases. Perhaps it will make it easier for us to share techniques across organizations and compare notes.&lt;/p&gt;
&lt;style&gt;
  html, body {
    overflow-x: hidden;
  }

  img {
    pointer-events: none;

  }

  pre {
    max-height: 550px;
  }

  #container {
    margin-top: -18em;
    /* border: 2px solid black; */
    /* border-radius: 6px; */
    z-index: 100;

    
  }
&lt;/style&gt;
&lt;script&gt;
  document.body.innerHTML = `&lt;div class=&quot;z-0 top-0 bg-cover bg-bottom static z-0&quot; style=&quot;width: 140vw; height: 600px; transform: rotate(7deg) translate(-100px, -175px); background-image: url(/img/model-spaghetti/everything.png);&quot;&gt;&lt;/div&gt;` + document.body.innerHTML;
  &lt;/script&gt;</content>
  </entry>
</feed>