<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="https://www.tebray.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.tebray.com/" rel="alternate" type="text/html" /><updated>2019-12-14T00:00:44+00:00</updated><id>https://www.tebray.com/feed.xml</id><title type="html">Tim Bray</title><subtitle>Geek. Learner. Coder. CTO. Cofounder.</subtitle><entry><title type="html">Real knowledge is to know the extent of one’s ignorance</title><link href="https://www.tebray.com/2019/07/28/Real-Knowledge.html" rel="alternate" type="text/html" title="Real knowledge is to know the extent of one's ignorance" /><published>2019-07-28T00:00:00+00:00</published><updated>2019-07-28T00:00:00+00:00</updated><id>https://www.tebray.com/2019/07/28/Real-Knowledge</id><content type="html" xml:base="https://www.tebray.com/2019/07/28/Real-Knowledge.html">&lt;p&gt;Confucius said in 500BC “Real knowledge is to know the extent of one’s ignorance”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/glen-noble-o4-YyGi5JBc-unsplash.jpg&quot; alt=&quot;image&quot; /&gt;
&lt;em&gt;Photo by Glen Noble on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This concept has come up in a few conversations recently.&lt;/p&gt;

&lt;p&gt;As your experience and knowledge increases across a range of topics, you may not feel any more knowledgeable. Why might this be? It turns out there’s a simple explanation.&lt;/p&gt;

&lt;p&gt;As your experience and knowledge expands, you encounter more topics and understand existing topics more deeply. Your circle of knowledge increases.&lt;/p&gt;

&lt;p&gt;On the edge of this knowledge circle are the topics you may have encountered, but don’t know. As your knowledge and experience increases, you know more topics exist.&lt;/p&gt;

&lt;p&gt;As your ‘knowns’ increase so do your ‘unknowns’. You know that you don’t know more :WINK:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/circle-of-knowledge.png&quot; alt=&quot;image&quot; /&gt;
&lt;em&gt;Circle of knowledge&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ok, so this model might explain it. How does this help, what can we do with this knowledge? And our newfound knowledge of our lack of knowledge!?&lt;/p&gt;

&lt;h2 id=&quot;imposter-syndrome&quot;&gt;Imposter syndrome&lt;/h2&gt;

&lt;p&gt;Imposter syndrome is a psychological pattern where you may doubt your accomplishments and fear being exposed as a fraud. You may attribute your success to luck and discount both your achievements and ability.&lt;/p&gt;

&lt;p&gt;With the above model, knowing what you don’t know plugs in here. Being more aware of this balance between knowledge &amp;amp; unknown can help you evaluate your true abilities and value your achievements.&lt;/p&gt;

&lt;h2 id=&quot;dunning-kruger-effect&quot;&gt;Dunning-Kruger effect&lt;/h2&gt;

&lt;p&gt;In the opposite direction is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Dunning–Kruger_effect&quot;&gt;Dunning–Kruger&lt;/a&gt; effect. This is a cognitive bias where people mistakenly assess their cognitive ability as greater than it is. Related to the &lt;a href=&quot;https://en.m.wikipedia.org/wiki/Illusory_superiority&quot;&gt;illusory superiority bias&lt;/a&gt;, a cognitive bias where people fail to recognise their lack of ability.&lt;/p&gt;

&lt;p&gt;I’ve encountered this issue regularly. The junior who over confidently proposes a risky or inappropriate solution. The manager who’s very strong opinion on a topic is misguided. Or the loud person at a party explaining something incorrectly.&lt;/p&gt;

&lt;h2 id=&quot;unknown-unknowns&quot;&gt;Unknown Unknowns&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/There_are_known_knowns&quot;&gt;Rumsfeld&lt;/a&gt; said it nicely with this quote&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Reports that say that something hasn’t happened are always interesting to me, because as we know, there are known knowns; there are things we know we know. We also know there are known unknowns; that is to say we know there are some things we do not know. But there are also unknown unknowns—the ones we don’t know we don’t know.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we understand the limits of our knowledge, we can make better decisions. Just identifying and recognising what we don’t know helps. We can then develop a plan to improve our knowledge, or at a minimum flag them as a risk.&lt;/p&gt;

&lt;h2 id=&quot;four-stages-of-knowledge&quot;&gt;Four Stages of Knowledge&lt;/h2&gt;

&lt;p&gt;There’s another model which explains progression of knowledge. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Four_stages_of_competence&quot;&gt;four stages of knowledge&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/four-stages-of-knowledge.png&quot; alt=&quot;image&quot; /&gt;
&lt;em&gt;Four stages of knowledge&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While there might be some ignorant bliss that goes with unconscious incompetence, it’s ideal to be aware of what stage your knowledge or skill is at. Then progress as many as possible through the stages, especially from the first stage!&lt;/p&gt;

&lt;p&gt;There’s still issues with model.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;For anything non-trivial, conscious competence is not a singular final state. Our knowledge is always incomplete. It either contains errors or the world has moved on. What was once true, now might not be.&lt;/p&gt;

&lt;p&gt;But we can use this knowledge to help us problem solve and make better decisions. Understanding what is most likely fact, what is probable, what is unlikely and then what is false.&lt;/p&gt;

&lt;p&gt;Build our self awareness and confidence in what we know. But we should also be humble. Be aware that we might be wrong, and be aware there is always more to learn.&lt;/p&gt;</content><author><name></name></author><summary type="html">Confucius said in 500BC “Real knowledge is to know the extent of one’s ignorance”.</summary></entry><entry><title type="html">Tracking software delivery and product velocity</title><link href="https://www.tebray.com/2019/06/02/Tracking-software-delivery.html" rel="alternate" type="text/html" title="Tracking software delivery and product velocity" /><published>2019-06-02T00:00:00+00:00</published><updated>2019-06-02T00:00:00+00:00</updated><id>https://www.tebray.com/2019/06/02/Tracking-software-delivery</id><content type="html" xml:base="https://www.tebray.com/2019/06/02/Tracking-software-delivery.html">&lt;p&gt;I’ve recently had a few discussions about velocity.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/mark-tuzman-103358-unsplash.jpg&quot; alt=&quot;image&quot; /&gt;
&lt;em&gt;Photo by Mark Tuzman on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The discussions were around how a company’s product velocity has been decreasing and how they aren’t delivering as fast as they need to. This is always a hot topic internally, as founders, leaders and teams are always looking to increase product velocity.&lt;/p&gt;

&lt;p&gt;While the discussions specifically talked about product velocity, they were talking more broadly about the ability to deliver value to customers.&lt;/p&gt;

&lt;p&gt;Agile made the term &lt;a href=&quot;https://en.wikipedia.org/wiki/Velocity_(software_development)&quot;&gt;velocity&lt;/a&gt; popular. It’s defined as &lt;strong&gt;unit of work&lt;/strong&gt; over an &lt;strong&gt;interval&lt;/strong&gt; (usually sprints).&lt;/p&gt;

&lt;p&gt;The tricky part with velocity is defining &lt;em&gt;unit of work&lt;/em&gt; - as it is usually a relative estimation, eg. story points. If estimations remain consistent over time, then velocity can be compared over time (they don’t need to be accurate, just consistent). Over a few sprints or months this can be accurate, but over longer periods the consistency declines. Estimates change as the team, projects, product and type of work change.&lt;/p&gt;

&lt;h2 id=&quot;the-need-for-additional-metrics&quot;&gt;The need for additional metrics&lt;/h2&gt;

&lt;p&gt;Without quality metrics, it’s all opinion. Usually it’s the loudest voice or most senior opinion that reigns, not the truth. Something I’ve experienced many times when I wasn’t tracking the right metrics.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If velocity is flawed as a metric, what data can we use to track software delivery?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All metrics can be flawed, so we want to combine multiple metrics across the full SDLC, hopefully creating a holistic picture. This should reduce bias and errors within individual metrics and give more reliable data to track software delivery over longer periods.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Customer Satisfaction&lt;/strong&gt;: &lt;a href=&quot;https://en.wikipedia.org/wiki/Net_Promoter&quot;&gt;NPS&lt;/a&gt; or similar to measure customer satisfaction. No point efficiently delivering features that customers don’t like.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Engineering&lt;/strong&gt;: &lt;a href=&quot;https://en.wikipedia.org/wiki/Lead_time&quot;&gt;Lead Time&lt;/a&gt; - The elapsed time between identifying a feature and delivery of the feature.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quality&lt;/strong&gt;: Issues found in production are a great indication of quality and a sustainable engineering process.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Production&lt;/strong&gt;: Mean time to resolve or repair (&lt;a href=&quot;https://en.wikipedia.org/wiki/Mean_time_to_repair&quot;&gt;MTTR&lt;/a&gt;) - how long it takes to address an issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;customer-satisfaction---nps&quot;&gt;Customer Satisfaction - NPS&lt;/h2&gt;

&lt;p&gt;Most important is to ensure we’re headed in the right direction. There’s not much value in efficiently delivering features that customers don’t use or need!&lt;/p&gt;

&lt;p&gt;There are plenty of metrics that measure customer satisfaction. While I believe NPS is flawed, it’s no more of less flawed that other metrics and is very easy to track.&lt;/p&gt;

&lt;h2 id=&quot;engineering---lead-time&quot;&gt;Engineering - Lead Time&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/images/lead-time.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Taken from Lean manufacturing principles. In the software world it’s the elapsed time between identifying a feature and delivery of the feature. The great thing about measuring lead time is that is encourages many good engineering practices:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Working in smaller batches - reduces risk &amp;amp; work in progress (WIP), increases throughput &amp;amp; iteration.&lt;/li&gt;
  &lt;li&gt;Encourages frequent deployment - as lead time measures when value is delivered to customers.&lt;/li&gt;
  &lt;li&gt;Backlog management - Evaluating adding items to a backlog &amp;amp; removing items that are not likely to ever be delivered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lead time can be optionally divided up into Cycle Time - the time between development starting and delivery. Measuring both Lead and Cycle allow tracking the time from the 3 core points of views - the customer/user (total time), product team (health of backlog) &amp;amp; engineering (time taken to build &amp;amp; deploy).&lt;/p&gt;

&lt;p&gt;While tracking lead time encourages deployments, it’s also valuable to track deployment frequency as another individual metric.&lt;/p&gt;

&lt;h2 id=&quot;quality---issues-found-in-production&quot;&gt;Quality - Issues found in production&lt;/h2&gt;

&lt;p&gt;Tracking quality can be a simple as recording how many bugs or issues have been found in production. If this goes up, the warning bells should start to ring loudly.&lt;/p&gt;

&lt;p&gt;Quality can also be tracked by deployment issues. Deployments need to be routine, tracking issues caused by release can give further insight into quality.&lt;/p&gt;

&lt;h2 id=&quot;production---mttr&quot;&gt;Production - MTTR&lt;/h2&gt;

&lt;p&gt;Once an issue is found in production, how long does it take to resolve? This is mean time to resolve (or recovery) - MTTR.&lt;/p&gt;

&lt;p&gt;What’s great about MTTR - like the above metrics - gives insight into the wider software engineering processes as well as production and customer impact.&lt;/p&gt;

&lt;h2 id=&quot;data-driven-decisions&quot;&gt;Data driven decisions&lt;/h2&gt;

&lt;p&gt;I like how these metrics all overlap &amp;amp; encourage a DevOps culture.&lt;/p&gt;

&lt;p&gt;They each give insight into the full product engineering process as well as each critical component (customer satisfaction, engineering efficiency, quality &amp;amp; production). The overlap increases the quality of each metric and increases the ability to track over time. Which is the whole aim — tracking software delivery or &lt;em&gt;velocity&lt;/em&gt; over time.&lt;/p&gt;

&lt;p&gt;When tracked over time, they give an early indication of software delivery issues or changes in product velocity. All based on real data, not opinion or hearsay!&lt;/p&gt;</content><author><name></name></author><summary type="html">I’ve recently had a few discussions about velocity.</summary></entry><entry><title type="html">Starting an Engineering culture</title><link href="https://www.tebray.com/2019/04/10/Starting-an-engineering-culture.html" rel="alternate" type="text/html" title="Starting an Engineering culture" /><published>2019-04-10T00:00:00+00:00</published><updated>2019-04-10T00:00:00+00:00</updated><id>https://www.tebray.com/2019/04/10/Starting-an-engineering-culture</id><content type="html" xml:base="https://www.tebray.com/2019/04/10/Starting-an-engineering-culture.html">&lt;p&gt;Over the past year I’ve been lucky enough to co-found a startup and build a range of products from scratch. While I’ve built many products from scratch, my previous most recent role was at the other end of the spectrum - with a mature company and products.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/sushobhan-badhai-372964-unsplash-2.jpg&quot; alt=&quot;image&quot; /&gt;
&lt;em&gt;Photo by Sushobhan Badhai on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s given me a great opportunity to think about what’s important to do when starting out. What patterns to watch out for. What decisions you can do early that will pay off later, what I’ve seen worked well, what didn’t work as well or wished we’d done differently.&lt;/p&gt;

&lt;p&gt;There are a range of obvious decisions which I won’t touch on - eg. lean/agile processes, modern architectures, platforms, CI/CD, monitoring, analytics etc. Instead, will try to cover some less obvious decisions and patterns to watch out for.&lt;/p&gt;

&lt;ol id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#conscious-trade-offs&quot; id=&quot;markdown-toc-conscious-trade-offs&quot;&gt;Conscious trade-offs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#constantly-attack-technical-debt&quot; id=&quot;markdown-toc-constantly-attack-technical-debt&quot;&gt;Constantly attack technical debt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#architect-for-change&quot; id=&quot;markdown-toc-architect-for-change&quot;&gt;Architect for change&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#define-done&quot; id=&quot;markdown-toc-define-done&quot;&gt;Define Done&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#culture-of-shipping&quot; id=&quot;markdown-toc-culture-of-shipping&quot;&gt;Culture of shipping&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#balance-change-and-stability&quot; id=&quot;markdown-toc-balance-change-and-stability&quot;&gt;Balance change and stability&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#diversity&quot; id=&quot;markdown-toc-diversity&quot;&gt;Diversity&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#summary&quot; id=&quot;markdown-toc-summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;conscious-trade-offs&quot;&gt;Conscious trade-offs&lt;/h2&gt;

&lt;p&gt;Should you write comprehensive unit tests for all code? Even if it is experimentation or prototyping and might not be released?&lt;/p&gt;

&lt;p&gt;When should you worry about scaling? Does performance matter when you don’t know what load to expect?&lt;/p&gt;

&lt;p&gt;Does this UI need to be a pixel perfect match to the design? Is it likely the design will change once customers start to use it?&lt;/p&gt;

&lt;p&gt;Juggling trade-offs are hard. But it’s our job to make these decisions. There’s no perfect answer, and the answer is likely to change over time as knowledge improves and the product matures. But, it’s easy for the answers to become cultural and questions stop being asked. Everything is done in the same way - regardless of whether it’s appropriate. Misalignment between teams starts to creep in, as expectations and understanding is different.&lt;/p&gt;

&lt;p&gt;What worked well for us was to ask these questions up front. Bake them into each story or feature and make sure everyone knows the tradeoffs and why.&lt;/p&gt;

&lt;h2 id=&quot;constantly-attack-technical-debt&quot;&gt;Constantly attack technical debt&lt;/h2&gt;

&lt;p&gt;Tech debt is the cost of future rework by taking shortcuts - not to be confused with plain bad code!&lt;/p&gt;

&lt;p&gt;If you haven’t worked on a green fields project recently, you may think it takes some time before tech debt accumulates. But, unless you have the luxury of time and know &lt;strong&gt;&lt;em&gt;everything&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;exactly&lt;/em&gt;&lt;/strong&gt; - there will be rework. We learn along the way. About the technology, platforms, services, market and customers.&lt;/p&gt;

&lt;p&gt;If we know tech debt is always accumulating, we should regularly pay it down. Not holding off and scheduling dedicated time in the future. In my experience, that future time will never arrive. Like paying off your mortgage, it’s best if you chip away paying weekly or monthly. Prioritise debt as part of each sprint.&lt;/p&gt;

&lt;p&gt;All tech debt isn’t created equally, it has different interest rates.&lt;/p&gt;

&lt;p&gt;Some of it has a high interest rate - like credit card debt - and is expensive to maintain. Finance #101 says pay off your high interest debt first. These are the items slowing the team down or causing issues.&lt;/p&gt;

&lt;p&gt;Other debt may have a lower interest rate and is cheaper to maintain. This debt may not be slowing the teams down or causing as many issues. Address this second.&lt;/p&gt;

&lt;p&gt;Scheduling a percentage of every sprint to addressing tech debt keeps it under control. The other approach I like is the “boy scout rule” - leave the campground cleaner than you found it. Made popular by Uncle Bob, whenever anyone touches something, make it better. Clean up the code, rename variables, add tests, do whatever improves it. Even if it’s only picking up one empty beer can or a bottle cap next to the campfire.&lt;/p&gt;

&lt;h2 id=&quot;architect-for-change&quot;&gt;Architect for change&lt;/h2&gt;

&lt;p&gt;Software architecture can be defined as the parts that are hard to change. They are a combination of factors - performance, security, scalability, requirements, data, compliance, and all “-ilities”.&lt;/p&gt;

&lt;p&gt;It’s sometimes easy to know which areas that are likely to change. They’re the areas you know the least about. The areas that are most unknown - from either a product feature or a technology point of view.&lt;/p&gt;

&lt;p&gt;I’ve also found the need to balance architectural design with a bias for action. When there are unknowns, it’s easy to over think and spend a lot of time guessing without improving outcomes. Make the best decision you can with the information at hand, within a &lt;em&gt;reasonable&lt;/em&gt; time frame and that is &lt;em&gt;reasonably&lt;/em&gt; easy to change in the future.&lt;/p&gt;

&lt;p&gt;I’m also a big believer in writing only the code you need to write. Leverage as many services &amp;amp; standard libraries as possible.&lt;/p&gt;

&lt;h2 id=&quot;define-done&quot;&gt;Define Done&lt;/h2&gt;

&lt;p&gt;Set a baseline early of what is done. Does it include tests, logging, monitoring, analytics? How about documentation and new feature announcements? Is releasing a feature enough, or does it need to be used - and what type of usage?&lt;/p&gt;

&lt;p&gt;It’s easy for steps to be forgotten, and teams to get out of sync. Writing down these expectations helps everyone stay on the same page.&lt;/p&gt;

&lt;h2 id=&quot;culture-of-shipping&quot;&gt;Culture of shipping&lt;/h2&gt;

&lt;p&gt;There appears to be a natural tendency in software for &lt;em&gt;everything&lt;/em&gt; to grow.&lt;/p&gt;

&lt;p&gt;Executives, project sponsors &amp;amp; product teams tend to increase scope. Engineering teams can struggle to break up tasks into smaller pieces and start to deliver less frequently in larger chunks. Pull request size increases, making it harder to review the changes. Risk increases.&lt;/p&gt;

&lt;p&gt;We know from Lean &amp;amp; Agile: batch size impacts delivery and increases risk. The larger the batch the more risk. I’ve noticed without external forces, batch sizes tend to increase and shipping slows down.&lt;/p&gt;

&lt;p&gt;Create a culture that breaks up tasks, values iteration and rewards shipping.&lt;/p&gt;

&lt;h2 id=&quot;balance-change-and-stability&quot;&gt;Balance change and stability&lt;/h2&gt;

&lt;p&gt;A lot has been written about the time it takes teams to perform and deliver results. For example, &lt;a href=&quot;https://en.wikipedia.org/wiki/Tuckman's_stages_of_group_development&quot;&gt;Tuckman’s&lt;/a&gt; forming, storming, norming &amp;amp; performing phases made popular by Lean &amp;amp; Agile.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/tuckman-team-phases.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We want to protect teams to ensure they reach the performing phase. But, there are challenges with too much stability.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Stability is also the default state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Beneficial team change requires work. Managers don’t like loosing their good people; people tend to prefer stability.&lt;/p&gt;

&lt;p&gt;With stability, teams can start to optimise locally, serving their own interests over the wider group. Or begin to lack diversity in thought - and not search for improvements or challenge preconceived ideas.&lt;/p&gt;

&lt;p&gt;We start to see tribalism creep in, where teams protect their patch. I’ve also seen conflict between teams, as teams understand their own situation but have little empathy or understanding of other teams challenges.&lt;/p&gt;

&lt;p&gt;Team stability also creates narrow experts or domain champions. Early on having experts is great, makes routing issues easy and they are usually highly productive. But it creates risk - relying on individuals, is hard to scale and can cause attrition or stagnation. A narrow expert will usually be an awesome contributor and since they are experts, there’s very little the wider team can contribute or provide feedback on.&lt;/p&gt;

&lt;p&gt;The ideal solution is to create a culture that expects some change, but not too much. Where teams are able to handle change and still perform. It’s difficult to balance and I’ve been caught out at both ends.&lt;/p&gt;

&lt;h2 id=&quot;diversity&quot;&gt;Diversity&lt;/h2&gt;

&lt;p&gt;We all know &lt;a href=&quot;https://www.mckinsey.com/business-functions/organization/our-insights/why-diversity-matters&quot;&gt;diversity&lt;/a&gt; helps companies and teams.&lt;/p&gt;

&lt;p&gt;A few years ago I caught myself complaining about lack of gender diversity in my teams. While working towards addressing this, I lost track of other diversity planes.&lt;/p&gt;

&lt;p&gt;During a high growth phase the business preferred &lt;em&gt;people that can hit the ground running&lt;/em&gt;. We were also hiring for individuals with a strong cultural fit. We ended up bringing on a number of very similar people and forgot about all the other diversity planes: background, age, education, seniority, experience, skills.&lt;/p&gt;

&lt;p&gt;A similar issue occurred with our referrals. Generally referrals are great by reducing some unknowns, but have limits, as people tend to refer alike people.&lt;/p&gt;

&lt;p&gt;Set your diversity goals early and monitor. It’s far easier to create diversity when teams are small, than to retrofit into larger teams.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;When you’re small and nimble it’s easier to change direction, adjust culture and fix issues. It’s the perfect time to start an engineering culture that delivers quality and produces real value.&lt;/p&gt;

&lt;p&gt;While not a comprehensive list of patterns, these items have definitely helped me.&lt;/p&gt;</content><author><name></name></author><summary type="html">Over the past year I’ve been lucky enough to co-found a startup and build a range of products from scratch. While I’ve built many products from scratch, my previous most recent role was at the other end of the spectrum - with a mature company and products.</summary></entry><entry><title type="html">MacOS - Your disk space is critically low</title><link href="https://www.tebray.com/2019/03/26/MacOS-diskspace-critical.html" rel="alternate" type="text/html" title="MacOS - Your disk space is critically low" /><published>2019-03-26T00:00:00+00:00</published><updated>2019-03-26T00:00:00+00:00</updated><id>https://www.tebray.com/2019/03/26/MacOS-diskspace-critical</id><content type="html" xml:base="https://www.tebray.com/2019/03/26/MacOS-diskspace-critical.html">&lt;p&gt;I’ve been receiving &lt;em&gt;Your disk space is critically low&lt;/em&gt; messages for the past couple months.&lt;/p&gt;

&lt;p&gt;It always occurs at a bad time, so I’d do the minimum. Clear downloads, empty the bin &amp;amp; sometimes reboot. This usually gave me about 10Gb free, which would last another few weeks, before I needed to repeat the process.&lt;/p&gt;

&lt;p&gt;Annoyingly, most of the disk was used by the System.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Time for a proper disk clean up!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are plenty of commercial tools that will help. But, wearing my engineer’s hat, I prefer to know how things work and wanted to do it manually.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/macos-storage.png&quot; alt=&quot;macos-storage&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Running through the process below, I was able to free up more than 50Gb of space.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Table of contents&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;the-basics&quot;&gt;The basics&lt;/h1&gt;

&lt;p&gt;Click on the &lt;em&gt;Apple&lt;/em&gt; icon in the menu, select &lt;em&gt;About this mac&lt;/em&gt; &amp;amp; then click on &lt;em&gt;Storage&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This gives a quick overview of what’s using your disk space.&lt;/p&gt;

&lt;p&gt;Click the &lt;em&gt;Manage…&lt;/em&gt; button and follow the recommendations.&lt;/p&gt;

&lt;h1 id=&quot;disk-usage-command&quot;&gt;Disk Usage command&lt;/h1&gt;

&lt;p&gt;It’s easy to see what folders are using up disk space - either by the command line or Finder GUI.&lt;/p&gt;

&lt;p&gt;In a terminal, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;du&lt;/code&gt; command to print out the disk usage of a folder or all folders.&lt;/p&gt;

&lt;p&gt;List disk space and in human readable form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-h&lt;/code&gt; and summarize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-s&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;du -sh .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finder also has a feature which displays folder sizes.&lt;/p&gt;

&lt;p&gt;Open Finder, press &lt;em&gt;(⌘) + J&lt;/em&gt; or select &lt;em&gt;View&lt;/em&gt; &amp;gt; &lt;em&gt;Show View Options&lt;/em&gt; from the menu. Then select the &lt;em&gt;Calculate all sizes&lt;/em&gt; options. The size column will now show folder sizes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/macos-finder-size.png&quot; alt=&quot;macos-storage&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;system-files---cache&quot;&gt;System Files - Cache&lt;/h1&gt;

&lt;p&gt;My user cache in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~\Library\Caches&lt;/code&gt; was very large.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;du -sh ~/Library/Caches/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Quit all apps before clearing anything in the cache. It’s apparently better to delete the contents of folders that the folders themselves - but I haven’t properly researched whether this is true. If you’re feeling lucky - clear it all, otherwise research the implications. I felt lucky &amp;amp; just deleted everything.&lt;/p&gt;

&lt;h1 id=&quot;node--yarn--npm&quot;&gt;Node / yarn / npm&lt;/h1&gt;

&lt;p&gt;If you’re using Nodejs, npm or yarn you may find a large cache has built up over time. I found my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn&lt;/code&gt; cache was over 8Gb and full of older packages. My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; cache also had a couple Gb which could be cleaned.&lt;/p&gt;

&lt;p&gt;Time for a clean:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yarn cache clean
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify &amp;amp; garbage collect npm as well:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm cache verify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;xcode-junk&quot;&gt;Xcode Junk&lt;/h1&gt;

&lt;h2 id=&quot;archives&quot;&gt;Archives&lt;/h2&gt;

&lt;p&gt;If you’re building and deploying iOS apps, you may have built up a large number of archives. I had 11Gb or archives - which was only about 5 months! These archives are only needed if the apps are still installed on customer devices (and then only if you haven’t pushed the symbols to your error tracking service - eg. Firebase/crashlytics/bugsnag/instabug etc.)&lt;/p&gt;

&lt;p&gt;Head to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Developer/Xcode/Archives&lt;/code&gt; and remove any folders you no longer need.&lt;/p&gt;

&lt;h2 id=&quot;support-for-older-devices&quot;&gt;Support for older devices&lt;/h2&gt;

&lt;p&gt;I had 15Gb of iOS Device Support. Most of these were for older iOS versions that we can safely delete.&lt;/p&gt;

&lt;p&gt;Browse to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Developer/Xcode/iOS DeviceSupport&lt;/code&gt; and remove older iOS version folders.&lt;/p&gt;

&lt;h2 id=&quot;downloads&quot;&gt;Downloads&lt;/h2&gt;

&lt;p&gt;Simulator images are also saved to this location &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Caches/com.apple.dt.Xcode/Downloads&lt;/code&gt;, delete any dmg files you don’t need&lt;/p&gt;

&lt;h2 id=&quot;old-simulator-runtimes&quot;&gt;Old simulator runtimes&lt;/h2&gt;

&lt;p&gt;Remove any old iOS versions you no use from here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Developer/CoreSimulator/Profiles/Runtimes&lt;/code&gt;&lt;/p&gt;

&lt;h1 id=&quot;cocoapods&quot;&gt;CocoaPods&lt;/h1&gt;

&lt;p&gt;The &lt;a href=&quot;https://cocoapods.org/&quot;&gt;CocoaPods&lt;/a&gt; cache can sometimes become large and dirty, you can delete it then re-download everything:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pod cache -all clean
pod update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re feeling lucky, can delete the whole pod cache folder here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Caches/CocoaPods&lt;/code&gt;&lt;/p&gt;</content><author><name></name></author><summary type="html">I’ve been receiving Your disk space is critically low messages for the past couple months.</summary></entry><entry><title type="html">6 tips using Apollo GraphQL</title><link href="https://www.tebray.com/2019/03/15/GraphQL-tips-and-tricks.html" rel="alternate" type="text/html" title="6 tips using Apollo GraphQL" /><published>2019-03-15T00:00:00+00:00</published><updated>2019-03-15T00:00:00+00:00</updated><id>https://www.tebray.com/2019/03/15/GraphQL-tips-and-tricks</id><content type="html" xml:base="https://www.tebray.com/2019/03/15/GraphQL-tips-and-tricks.html">&lt;p&gt;We’re using the awesome &lt;a href=&quot;https://www.apollographql.com&quot;&gt;Apollo&lt;/a&gt; client and server libraries in Node.js &amp;amp; React / React Native apps. They’ve been reliable, full featured and mostly easy to use - albeit with reasonable learning curve. Which is totally understandable since they are doing a lot for you.&lt;/p&gt;

&lt;p&gt;Here are a few items that we didn’t immediately see in the documentation, didn’t understand the importance of, or found we needed during development and roll out to production.&lt;/p&gt;

&lt;ol id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#graphql-vs-the-libraries&quot; id=&quot;markdown-toc-graphql-vs-the-libraries&quot;&gt;GraphQL vs the libraries&lt;/a&gt;    &lt;ol&gt;
      &lt;li&gt;&lt;a href=&quot;#graphql-playground&quot; id=&quot;markdown-toc-graphql-playground&quot;&gt;GraphQL Playground&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#raw-graphql&quot; id=&quot;markdown-toc-raw-graphql&quot;&gt;Raw GraphQL&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#debugging-with-postman--chrome-dev-tools&quot; id=&quot;markdown-toc-debugging-with-postman--chrome-dev-tools&quot;&gt;Debugging with Postman &amp;amp; Chrome dev tools&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#logging&quot; id=&quot;markdown-toc-logging&quot;&gt;Logging&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#simple-authentication-and-authorization&quot; id=&quot;markdown-toc-simple-authentication-and-authorization&quot;&gt;Simple Authentication and Authorization&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#caching&quot; id=&quot;markdown-toc-caching&quot;&gt;Caching&lt;/a&gt;    &lt;ol&gt;
      &lt;li&gt;&lt;a href=&quot;#our-caching-approach&quot; id=&quot;markdown-toc-our-caching-approach&quot;&gt;Our caching approach&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#querying-only-selected-fields-from-the-database&quot; id=&quot;markdown-toc-querying-only-selected-fields-from-the-database&quot;&gt;Querying only selected fields from the database&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#summary&quot; id=&quot;markdown-toc-summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;graphql-vs-the-libraries&quot;&gt;GraphQL vs the libraries&lt;/h1&gt;

&lt;p&gt;The Apollo libraries do a lot of heavy lifting for you. This makes it easier to get started, but with this help and abstraction you can easily get lost as to what the libraries are doing and what is just plain GraphQL. We found it important to understand what functionality was provided by the libraries vs what is plain GraphQL. It’s worth reading (or at least skimming) the official &lt;a href=&quot;https://facebook.github.io/graphql/draft&quot;&gt;GraphQL spec&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;graphql-playground&quot;&gt;GraphQL Playground&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/features/graphql-playground.html&quot;&gt;playground&lt;/a&gt; is invaluable in playing around with queries and mutations.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Add your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; HTTP header to query anything that is not public in your graph/api.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;Authorization&quot;: &quot;Bearer eyJhbGciOiJIUzI.....&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;raw-graphql&quot;&gt;Raw GraphQL&lt;/h3&gt;

&lt;p&gt;Tools like the playground make it easy to get comfortable with the language by viewing, executing and testing GraphQL. It’s also worth noting that you don’t need to use the client libraries at all.&lt;/p&gt;

&lt;p&gt;Once you understand the language syntax, you can also write queries or mutations without using the client libraries at all. For example, here we’re calling a mutation using plain old JavaScript.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;refreshToken&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;apiUri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;apiUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;operationName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;authRefresh&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`mutation authRefresh {
                authRefresh {
                  ok
                  data
                }
              }`&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;debugging-with-postman--chrome-dev-tools&quot;&gt;Debugging with Postman &amp;amp; Chrome dev tools&lt;/h1&gt;

&lt;p&gt;For further debugging head to Chrome’s network tools. Like any network call, you can see the full GraphQL request and response under the network tab.&lt;/p&gt;

&lt;p&gt;Copy the request as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cURL&lt;/code&gt; and directly import into the great &lt;a href=&quot;https://www.getpostman.com&quot;&gt;Postman&lt;/a&gt; API editor (or API IDE?) for further debugging or manipulation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/graphql-chrome-dev-tools-copy.png&quot; alt=&quot;graphql_chrome_tools&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;logging&quot;&gt;Logging&lt;/h1&gt;

&lt;p&gt;It’s essential to log errors and requests. We found with the default setup, syntax errors and other issues became hard to debug unless we riddled our code with exception handling. We then found this &lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/features/errors.html#Masking-and-logging-errors&quot;&gt;feature&lt;/a&gt; of the Apollo server constructor, which allows logging (&amp;amp; modifying) errors and responses. This greatly helps with diagnosing syntax errors or issues that occur outside your resolvers.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ApolloServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;typeDefs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;graphQLContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// filter whatever errors your don't want to log&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`[GraphQL.error] &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// don't log auth mutations or schema requests&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;authSignIn&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;authRefreshToken&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__schema&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`[GraphQL.response] &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;()`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are more advanced logging techniques, but this is super simple to use from day 1 and can save a few debugging headaches.&lt;/p&gt;

&lt;h1 id=&quot;simple-authentication-and-authorization&quot;&gt;Simple Authentication and Authorization&lt;/h1&gt;

&lt;p&gt;The GraphQL specification doesn’t cover authentication or authorization. It’s up to you how to implement it, which is great (you can choose whatever method best suits your situations), and could be annoying (as there isn’t a single best practice solution).&lt;/p&gt;

&lt;p&gt;When starting our project, we initially had all GraphQL operations (API calls) public. We then added simple authentication via Express middleware, and started to pass an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isAuthenticated&lt;/code&gt; flag down to all query and mutation resolvers.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AuthenticationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not authenticated.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While this works fine, it starts to get very messy as soon as the project started to grow. We then centralised these checks into the context using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gql&lt;/code&gt; to parse the incoming operation. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gql&lt;/code&gt; command takes in the GraphQL string operation and returns a GraphQL object - with operation type, operation name and selected fields (via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;selectionSet&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ApolloServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;typeDefs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;graphQLContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;graphQLContext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// use a express middleware to auth user&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getOperationName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Check here if this a public operation&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isPublicOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AuthenticationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Not authenticated.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Could also check role based authorization&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;currentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getOperationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;// use gql to parse the given query/mutation&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;gql&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// this is the name the user gave for the operation&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operationNameDefault&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;body.operationName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// this is the name of the actual operation&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;definitions[0].selectionSet.selections[0].name.value&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operationNameDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;definitions[0].operation&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are more advanced authentication and authorization techniques, like using GraphQL &lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/features/authentication.html#directives-auth&quot;&gt;schema directives&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;caching&quot;&gt;Caching&lt;/h1&gt;

&lt;p&gt;Early on, we ran into issues when our logic became complicated and we started dealing with eternal systems/API’s. We needed to send out requests to multiple systems, then wait and combine the responses.&lt;/p&gt;

&lt;p&gt;The natural tendency - especially coming from REST based API’s - was to just query more often. We would re-factor components to force this and changed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchPolicy&lt;/code&gt; for queries to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;network-only&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We were working against GraphQL &amp;amp; Apollo. The solution to this growing complexity was to leverage the Apollo Cache (and subscriptions or polling)&lt;/p&gt;

&lt;p&gt;The default &lt;a href=&quot;https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-fetchPolicy&quot;&gt;fetchPolicy&lt;/a&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache-first&lt;/code&gt; (there could be an argument that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache-and-network&lt;/code&gt; should be the default). Cache-first checks if the requested data is in the cache, and if so, just returns it. It’s up to you to figure out if that data might be stale.&lt;/p&gt;

&lt;p&gt;There are a few options to dealing with potentially stale data:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Periodically clear the whole cache&lt;/li&gt;
  &lt;li&gt;Bypass the cache using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;network-only&lt;/code&gt; (or request a network call after checking the cache &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache-and-network&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Prevent the cache becoming stale (see below for techniques)&lt;/li&gt;
  &lt;li&gt;Set the cache directives using the &lt;a href=&quot;https://github.com/apollographql/apollo-cache-control&quot;&gt;cache control extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s important to set up the cache when creating the Apollo Client. If you are using guid’s then it’s as easy as this, otherwise check out the &lt;a href=&quot;https://www.apollographql.com/docs/react/advanced/caching.html&quot;&gt;InMemoryCache options&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ApolloClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;InMemoryCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;dataIdFromObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once we understood how the cache works, everything became easier.&lt;/p&gt;

&lt;h3 id=&quot;our-caching-approach&quot;&gt;Our caching approach&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Understand &lt;em&gt;exactly&lt;/em&gt; how each of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchPolicy&lt;/code&gt; values work&lt;/li&gt;
  &lt;li&gt;Install the Chrome &lt;a href=&quot;https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm&quot;&gt;Apollo Client Developer&lt;/a&gt; extension. This allows inspecting the cache as well as queries and mutations.&lt;/li&gt;
  &lt;li&gt;Consider the cache on every single call.
    &lt;ul&gt;
      &lt;li&gt;Does it matter if this data comes from the cache?&lt;/li&gt;
      &lt;li&gt;Or does it need to only come from the server?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Keep track of what variables are used for queries. Each query is cached along with it’s variables&lt;/li&gt;
  &lt;li&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchMore&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@connection&lt;/code&gt; directive to handle &lt;a href=&quot;https://www.apollographql.com/docs/react/advanced/caching.html#fetchMore&quot;&gt;paginated data&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Consider the cache after every &lt;em&gt;mutation&lt;/em&gt;.
    &lt;ul&gt;
      &lt;li&gt;If we return the object that was updated/created, Apollo will automatically cache it for us.&lt;/li&gt;
      &lt;li&gt;Manually handle when we insert new records.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Determine if it’s better to update the cache manually after a mutation using &lt;a href=&quot;https://www.apollographql.com/docs/react/advanced/caching.html#writequery-and-writefragment&quot;&gt;writeQuery&lt;/a&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;refetch&lt;/code&gt; the data from the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;querying-only-selected-fields-from-the-database&quot;&gt;Querying only selected fields from the database&lt;/h1&gt;

&lt;p&gt;One item that annoyed us early on with our implementation was the number of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT * FROM ...&lt;/code&gt; occurring in our solution. We’re using a TypeORM (which is a solid ORM, still with all the tradeoffs and benefits of ORM’s..).&lt;/p&gt;

&lt;p&gt;Here’s a super simple example.&lt;/p&gt;

&lt;p&gt;Server&lt;/p&gt;
&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// define the graphql query&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserQueryDefs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;gql&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`
  extend type Query {
    user(id: ID): User
  }
`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// graphql resolver&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// return the requested user (returns the full User entity)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;findOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Client&lt;/p&gt;
&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// # graphql query operation requesting name field&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
  &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running the query above will select all fields from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt; table (via the TypeORM &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findOne&lt;/code&gt; method), then GraphQL Apollo server will throw away all the fields other than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; and return just it to the client.&lt;/p&gt;

&lt;p&gt;There is a solution to this, leveraging the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;info&lt;/code&gt; parameter on &lt;a href=&quot;https://www.apollographql.com/docs/graphql-tools/resolvers#Resolver-function-signature&quot;&gt;resolvers&lt;/a&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;info&lt;/code&gt; parameter isn’t documented and Apollo recommend it should be used for advanced cases (we’ll see why shortly).&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// graphql resolver which only selects &lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// get an array of selected fields &lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;selectedFields&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fieldNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selectionSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;// return the requested user - with only the selected fields&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;findOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;selectedFields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are a few issues with the solution.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Only works on the first level of the query. For example, this won’t handle situations where related entities are also requested. This can be solved through recursion.&lt;/li&gt;
  &lt;li&gt;Doesn’t handle fragments.&lt;/li&gt;
  &lt;li&gt;Using an undocumented feature of Apollo server, which may change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This could be a great solution make some key operations as efficient as possible, without creating separate operations. But should only be used carefully with an understanding of the trade-offs.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;These are a few tips and tricks that we picked up over time. Hopefully you’ll find some of them useful.&lt;/p&gt;

&lt;p&gt;Please note, these examples are all simplifications, and are not production ready!&lt;/p&gt;</content><author><name></name></author><summary type="html">We’re using the awesome Apollo client and server libraries in Node.js &amp;amp; React / React Native apps. They’ve been reliable, full featured and mostly easy to use - albeit with reasonable learning curve. Which is totally understandable since they are doing a lot for you.</summary></entry><entry><title type="html">GraphQL and React</title><link href="https://www.tebray.com/2019/02/20/GraphQL-React.html" rel="alternate" type="text/html" title="GraphQL and React" /><published>2019-02-20T00:00:00+00:00</published><updated>2019-02-20T00:00:00+00:00</updated><id>https://www.tebray.com/2019/02/20/GraphQL-React</id><content type="html" xml:base="https://www.tebray.com/2019/02/20/GraphQL-React.html"></content><author><name></name></author><summary type="html"></summary></entry><entry><title type="html">GraphQL primer.</title><link href="https://www.tebray.com/2018/12/10/GraphQL-intro.html" rel="alternate" type="text/html" title="GraphQL primer." /><published>2018-12-10T00:00:00+00:00</published><updated>2018-12-10T00:00:00+00:00</updated><id>https://www.tebray.com/2018/12/10/GraphQL-intro</id><content type="html" xml:base="https://www.tebray.com/2018/12/10/GraphQL-intro.html">&lt;p&gt;When we started evaluating GraphQL for a new project, it seemed to have as many detractors as fans.&lt;/p&gt;

&lt;p&gt;The GraphQL tag line explains it as &lt;em&gt;a query language for your api&lt;/em&gt;. But I’m not sure that really helps explain what it does, how it can help and whether it’s a good option. There’s also misunderstanding (and some misinformation) out there.&lt;/p&gt;

&lt;p&gt;We ended up selecting it as our API platform, and have built up reasonable experience with the technology. We’ve learnt a lot of hard lessons along the way, which I’ll explain in this and a few upcoming posts.&lt;/p&gt;

&lt;h2 id=&quot;what-is-it&quot;&gt;What is it&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s a query language or way to describe data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Around this language are a range of libraries and tools - eg. servers, clients, services etc. These all combine to enable an API (or query system) which decouples services &amp;amp; allow us to ship value. Boom! Or so it says on the box.&lt;/p&gt;

&lt;p&gt;We’ve been working heavily in the JS world (React, React Native, Node), so selected the defacto standard implementation of GraphQL by &lt;a href=&quot;https://www.apollographql.com&quot;&gt;Apollo&lt;/a&gt;. Apollo builds the popular Apollo Client and Server libraries - which have over 300,000 downloads per week.&lt;/p&gt;

&lt;p&gt;Apollo also offer paid services on top the platform. These services are totally optional though. You can use the open source libraries for free :-)&lt;/p&gt;

&lt;h2 id=&quot;why-not-just-use-rest&quot;&gt;Why not just use REST&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Strongly typed schema for free. 
That’s nearly enough of a reason right there. Having a &lt;em&gt;contract&lt;/em&gt; is essential to building robust applications. GraphQL comes with a built in schema which contains all the operations (queries and mutations/updates), fields and data types.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Specify just the data you want in one operation.
With REST, you typically have to fire off a number of calls, collate the data and then throw away the results you don’t need. With GraphQL you can specify just the data you need in one single call.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This does depend on your Schema design. With GraphQL you need to think about your API totally differently, not in single end points, but in graphs.&lt;/p&gt;

&lt;h2 id=&quot;where-to-start&quot;&gt;Where to start&lt;/h2&gt;

&lt;p&gt;We would’ve like these &lt;a href=&quot;https://principledgraphql.com&quot;&gt;Principles&lt;/a&gt; to be around when we started with GraphQL. It could’ve prevented some wasted time, debate and mistakes. They’re a great starting point:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Integrity&lt;/strong&gt;: one graph, federated &amp;amp; tracked in a registry.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Agility&lt;/strong&gt;: build the graph up based on real needs, when needed. Just like evolutionary architecture!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Operations&lt;/strong&gt;: control access, layer and advanced logging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Probably should read the &lt;a href=&quot;https://facebook.github.io/graphql/draft&quot;&gt;official spec&lt;/a&gt;, or at least parts of it. It helps to understand what is GraphQL vs what is provided by libraries and third party add-ons.&lt;/p&gt;

&lt;h2 id=&quot;common-objections&quot;&gt;Common objections&lt;/h2&gt;

&lt;p&gt;While GraphQL has now been around for some time (available publicly since 2015) and it’s usage is widespread, there are still plenty of detractors, misinformation and varied approaches.&lt;/p&gt;

&lt;p&gt;One criticism we heard frequently is around losing control of your back-end. Front end engineers writing random inefficient queries. Will you loose control of data and back-end services?&lt;/p&gt;

&lt;p&gt;To fuel that fire, there are tools like &lt;a href=&quot;https://hasura.io&quot;&gt;this&lt;/a&gt; which layer GraphQL directly on top of the database. They may be good for prototyping and some use cases, but the &lt;a href=&quot;https://principledgraphql.com/operations#10-separate-the-graphql-layer-from-the-service-layer&quot;&gt;recommended approach&lt;/a&gt; is toward clear separation.&lt;/p&gt;

&lt;p&gt;The power of GraphQL is highlighted when connecting a range of disparate services - which can be GraphQL, REST or really anything.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/graphql-architecture.png&quot; alt=&quot;graphql_architecture&quot; /&gt;
Image taken from this &lt;a href=&quot;https://labs.getninjas.com.br/sharing-data-in-a-microservices-architecture-using-graphql-97db59357602&quot;&gt;great article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This separation allows GraphQL to be an API gateway. This solves a range of issues - and works in very well with micro-services. A single endpoint for clients but full control over data owners / teams / products on the back-end. This approach also enables greater control over data loading, caching, load balancing and error handling.&lt;/p&gt;

&lt;h2 id=&quot;next&quot;&gt;Next&lt;/h2&gt;

&lt;p&gt;Like every technology, GraphQL is not the perfect choice for every situation. If you have a very simple API that is unlikely to change, then REST is probably the best solution. If you have a larger, more complex API that will grow in size and complexity over time, then GraphQL might be the right choice.&lt;/p&gt;

&lt;p&gt;In the next few posts, I’ll write up some of our lessons learnt.&lt;/p&gt;</content><author><name></name></author><summary type="html">When we started evaluating GraphQL for a new project, it seemed to have as many detractors as fans.</summary></entry><entry><title type="html">Why we switched to GitLab</title><link href="https://www.tebray.com/2018/09/01/Gitlab.html" rel="alternate" type="text/html" title="Why we switched to GitLab" /><published>2018-09-01T00:00:00+00:00</published><updated>2018-09-01T00:00:00+00:00</updated><id>https://www.tebray.com/2018/09/01/Gitlab</id><content type="html" xml:base="https://www.tebray.com/2018/09/01/Gitlab.html">&lt;p&gt;One of the first services you sign up to when creating software is an online Source Control Management (SCM) service. &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt; is the largest and the most well-known, and was the first service we signed up to.&lt;/p&gt;

&lt;p&gt;One of my colleagues suggested we try &lt;a href=&quot;https://gitlab.com&quot;&gt;GitLab&lt;/a&gt;. I was initially apprehensive - I like to use standards and whatever the masses are using. I use &lt;a href=&quot;https://bitbucket.org&quot;&gt;BitBucket&lt;/a&gt; for personal projects (and liked their free unlimited private repositories - up to 5 users), but hadn’t ever used GitLab.&lt;/p&gt;

&lt;p&gt;What made the decision easier was …pricing! GitLab has an awesome free plan: Unlimited private projects and collaborators for free. We initially would have at least a dozen contributors on our project, so this sealed the deal!&lt;/p&gt;

&lt;h2 id=&quot;initial-impressions&quot;&gt;Initial Impressions&lt;/h2&gt;

&lt;p&gt;Git is Git. At least from the command line, or through a GUI (I tend to use SourceTree for visualizing changes). So initially it makes no difference what service you use. It’s the add-ons and the extra functionality that really differentiate the choices.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/quinten-de-graaf-410449-unsplash.jpg&quot; alt=&quot;Pipelines&quot; /&gt;
Photo by Quinten de Graaf on Unsplash&lt;/p&gt;

&lt;p&gt;GitLab, BitBucket &amp;amp; GitHub offer Issue tracking &amp;amp; documentation (via wiki), but GitLab has taken a different approach by including CI/CD pipelines.&lt;/p&gt;

&lt;h2 id=&quot;continuous-integration--delivery-pipelines&quot;&gt;Continuous Integration &amp;amp; Delivery Pipelines&lt;/h2&gt;

&lt;p&gt;GitLab &lt;a href=&quot;https://about.gitlab.com/product/continuous-integration/&quot;&gt;CI/CD Pipelines&lt;/a&gt; enable building, testing, deploying &amp;amp; monitoring code. There’s a lot going here. Back in my previous post &lt;a href=&quot;/2018/06/01/File-New-Project.html&quot;&gt;File &amp;gt; New Project&lt;/a&gt; I said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“It’s amazing how much CI/CD improves outcomes by encouraging regular delivery of small pieces of functionality. The DevOps mindset.” - Tim Bray, 2018&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/images/cicd_pipeline_infograph.png&quot; alt=&quot;cicd_pipelines&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s essential for successful projects. Using this DevOps mindset from the start of a project is super helpful. Having this built into the platform makes a lot of sense. As long as it works…&lt;/p&gt;

&lt;p&gt;It’s super easy to get started just put a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.yml&lt;/code&gt; file into the root of your project and configure a Runner to execute the commands. The yml file will contain options (eg. branches to monitor) and build, test or deployment commands. Whenever GitLab sees a new commit or push in a monitored branch, it will start a new Runner and execute the appropriate commands from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;I’m not a fan of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yml&lt;/code&gt;, but it is pretty easy to understand. What is a painful, is testing your configuration. This is usually some trial and error, make a change, commit/push, watch how it worked &amp;amp; repeat. Although, GitLab does have a CI Lint tool that checks if your yml is valid.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;I expected to use another tool for our CI/CD piplines. Having this inbuilt into GitLab enabled us to get our pipelines up and running quickly. Can’t argue with the price either.&lt;/p&gt;

&lt;p&gt;I expect in future we’ll outgrow GitLab’s CI/CD and want to use something more sophisticated, but until then we’ll concentrate on coding and building our software.&lt;/p&gt;</content><author><name></name></author><summary type="html">One of the first services you sign up to when creating software is an online Source Control Management (SCM) service. GitHub is the largest and the most well-known, and was the first service we signed up to.</summary></entry><entry><title type="html">Technology choices</title><link href="https://www.tebray.com/2018/08/01/Technology-choices.html" rel="alternate" type="text/html" title="Technology choices" /><published>2018-08-01T00:00:00+00:00</published><updated>2018-08-01T00:00:00+00:00</updated><id>https://www.tebray.com/2018/08/01/Technology-choices</id><content type="html" xml:base="https://www.tebray.com/2018/08/01/Technology-choices.html">&lt;p&gt;In my &lt;a href=&quot;/2018/06/01/File-New-Project.html&quot;&gt;last post&lt;/a&gt; I talked about some of my thinking around choosing technology and how it fits alongside solid processes and tooling.&lt;/p&gt;

&lt;p&gt;This post outlines some technology I’ve been using recently.&lt;/p&gt;

&lt;h2 id=&quot;tools-and-platforms&quot;&gt;Tools and platforms&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;gitlab - git, issues, CI/CD&lt;/li&gt;
  &lt;li&gt;Google cloud platform (GCP)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;core-technology&quot;&gt;Core technology&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;postgres (hosted on GCP)&lt;/li&gt;
  &lt;li&gt;nodejs&lt;/li&gt;
  &lt;li&gt;graphql&lt;/li&gt;
  &lt;li&gt;React&lt;/li&gt;
  &lt;li&gt;React Native for building iOS &amp;amp; Android apps&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;additional-services--tools&quot;&gt;Additional services / tools&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Senty&lt;/li&gt;
  &lt;li&gt;Stackdriver&lt;/li&gt;
  &lt;li&gt;Firebase&lt;/li&gt;
  &lt;li&gt;Fastlane for publishing mobile apps&lt;/li&gt;
  &lt;li&gt;Prettier.io - formatting code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, I’ll try to build out the reasoning behind each selection.&lt;/p&gt;</content><author><name></name></author><summary type="html">In my last post I talked about some of my thinking around choosing technology and how it fits alongside solid processes and tooling.</summary></entry><entry><title type="html">File &amp;gt; New Project</title><link href="https://www.tebray.com/2018/06/01/File-New-Project.html" rel="alternate" type="text/html" title="File &gt; New Project" /><published>2018-06-01T00:00:00+00:00</published><updated>2018-06-01T00:00:00+00:00</updated><id>https://www.tebray.com/2018/06/01/File-New-Project</id><content type="html" xml:base="https://www.tebray.com/2018/06/01/File-New-Project.html">&lt;p&gt;I’ve been starting quite a few new software projects lately - &lt;em&gt;yea&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;It’s given me a chance to reflect on how to do things right from the start. But what is right? And how do we navigate all the great technology choices we have today?&lt;/p&gt;

&lt;h2 id=&quot;what-is-right&quot;&gt;What is right?&lt;/h2&gt;

&lt;p&gt;I like to have a solid methodology and rules. This helps to generate results that are predictable and repeatable. For example, I quite like the &lt;a href=&quot;https://12factor.net&quot;&gt;12 Factor App&lt;/a&gt; methodologies, and &lt;em&gt;mostly&lt;/em&gt; follow the factors.&lt;/p&gt;

&lt;p&gt;While I don’t really believe in absolutes, but here are some of my some of my non-negotiables* in software projects:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Modern architectures - break that project up, don’t build another monolith. I’m also a fan of evolutionary architecture, design for change.&lt;/li&gt;
  &lt;li&gt;Modern languages - I like to choose modern languages, but not bleeding edge. Don’t want to start a new project on legacy. But don’t want to build something where it may be impossible to get help or build a team around - when the project inevitably succeeds :-)&lt;/li&gt;
  &lt;li&gt;Code formatters, linters &amp;amp; static analysis - use these to reduce style issues and improve code quality&lt;/li&gt;
  &lt;li&gt;Modern Platforms - choose platforms that are going to accelerate development. I don’t want to be building anything that can be pulled ‘off-the-shelf’.&lt;/li&gt;
  &lt;li&gt;API languages - how are all the services going to talk to each other? There are some cool options now. SOAP is not one of them ;-)&lt;/li&gt;
  &lt;li&gt;Source control service - In my first coding job, source control was managed by using different disks and folders: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup #1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest backup&lt;/code&gt; etc. It was a nightmare, luckily was able to convince the boss there was a better way. Now it’s not whether to use source control, or what type of source control (ie use Git), it’s just about what service to use and what extras it provides.&lt;/li&gt;
  &lt;li&gt;Issue tracking software - some great options here these days, it’s also included in most source control services. I like to have the git comments link back to issues and stories.&lt;/li&gt;
  &lt;li&gt;Documentation - somewhere to write doco - internal &amp;amp; external. Nuff said.&lt;/li&gt;
  &lt;li&gt;CI/CD - It should be super easy to get changes tested and deployed to production through pipelines. It’s amazing how much this improves outcomes by encouraging regular delivery of small pieces of functionality. The DevOps mindset.&lt;/li&gt;
  &lt;li&gt;Monitoring - Need to know when there are issues and how the app is performing. There are now so many great tools and services to choose from here.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s amazing how much this improves outcomes by encouraging regular delivery of small pieces of functionality. The DevOps mindset.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(*) &lt;em&gt;Rules are there to be broken, I just try to consciously balance the trade-offs. I’ve recently chosen a range of technologies that I didn’t have much experience with and somewhere very new. Mostly because I wanted to learn, improve my skills and I expect the technology to gain popularity.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;bias-for-action&quot;&gt;Bias for action&lt;/h2&gt;

&lt;p&gt;There are dozens of good options for every one of the above. It’s easy to get distracted on research. Especially since I’ve suffered from my previous choices. This time I won’t make that mistake (I’ll just make another one instead…)&lt;/p&gt;

&lt;p&gt;I value researching - reading, talking to people, playing around &amp;amp; prototyping. Plus research is easy and fun! But it’s also easy to blow days or weeks figuring out the perfect option and be no closer to selecting the right tech that will be pivotal for a new project.&lt;/p&gt;

&lt;p&gt;After I have a certain level of confidence in a technology or direction, I prefer to make a start and learn along the journey. I also like to asses the choices regularly - especially when it early enough to change direction.&lt;/p&gt;

&lt;p&gt;My bias for action has bitten me a few times recently. Where I’ve jumped in and started designing, architecting and building before I fully understood the technology. Most of the issues that arose are easy fixes, I just could’ve saved a little time and hassle with a better understanding.&lt;/p&gt;

&lt;h2 id=&quot;be-happy-with-the-choices&quot;&gt;Be happy with the choices&lt;/h2&gt;

&lt;p&gt;It’s easier to change technology choices now than ever before.&lt;/p&gt;

&lt;p&gt;The Cloud, micro services, React/Angular/Vue components, shared packages (npm/gems etc) and interoperability between systems makes it easier to swap out parts of a platform than ever before.&lt;/p&gt;

&lt;h2 id=&quot;its-all-secondary&quot;&gt;It’s all secondary&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;…they are secondary to having the right processes, culture, automation &amp;amp; measurement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While choosing great technologies and tools will help a project succeed, I’ve found they are secondary to having the right processes, culture, automation &amp;amp; measurement. These enable that consistent quality and delivery. Building the right product in the right way.&lt;/p&gt;</content><author><name></name></author><summary type="html">I’ve been starting quite a few new software projects lately - yea!</summary></entry></feed>