<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Erik Pukinskis</title>
    <description>The latest articles on DEV Community by Erik Pukinskis (@erikpuk).</description>
    <link>https://dev.to/erikpuk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1073626%2F96211499-11af-4375-a018-38ed3c19c1c5.png</url>
      <title>DEV Community: Erik Pukinskis</title>
      <link>https://dev.to/erikpuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/erikpuk"/>
    <language>en</language>
    <item>
      <title>How to "own" a project if you're an engineer</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Fri, 31 Jan 2025 16:55:38 +0000</pubDate>
      <link>https://dev.to/erikpuk/how-to-own-a-project-if-youre-an-engineer-12d5</link>
      <guid>https://dev.to/erikpuk/how-to-own-a-project-if-youre-an-engineer-12d5</guid>
      <description>&lt;p&gt;We're working through the &lt;a href="https://dev.to/erikpuk/four-roles-your-software-team-cant-function-without-2b5f"&gt;Four roles your software team can't function without&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Last time we talked about the &lt;a href="https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8"&gt;Bug Warden&lt;/a&gt;, but perhaps the most important is the "Epic Owner".&lt;/p&gt;

&lt;h2&gt;
  
  
  Why engineers should own epics
&lt;/h2&gt;

&lt;p&gt;A software project goes through many phases with different collaborators involved in each phase:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Requirements gathering — &lt;strong&gt;PM&lt;/strong&gt; leads, with user researcher supporting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Design — &lt;strong&gt;Designer&lt;/strong&gt; leads with PM supporting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Technical planning — &lt;strong&gt;Engineer&lt;/strong&gt; leads, designer supporting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prioritization — &lt;strong&gt;PM&lt;/strong&gt; decides when projects are ready to go into production&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Development — A team of &lt;strong&gt;engineers&lt;/strong&gt; lead, designer providing clarification and PM providing making strategic calls&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Testing — &lt;strong&gt;QA&lt;/strong&gt; leads, with engineers supporting and PM validating&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Release &amp;amp; validation — &lt;strong&gt;PM&lt;/strong&gt; leads, with engineers supporting&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In theory, anyone could be the central coordinator throughout this process... you could make an argument for a PM doing it (to shepherd the business goals), a designer (to shepherd the user experience), or even a project manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And frankly, if someone else on your team is already doing a great job coordinating all of this stuff, &lt;em&gt;let them&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But it's much more common that the individual contributors around you are focused on their... individual contributions. And there's a void for someone to step up and do some leadership.&lt;/p&gt;

&lt;p&gt;If you're an engineer, and you want to show that you're more than just a grunt, that you're taking responsibility, and &lt;em&gt;owning the outcome&lt;/em&gt; then you might want to take on the Epic Owner role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six process touchpoints the Epic Owner owns
&lt;/h2&gt;

&lt;p&gt;Fundamentally, owning an epic means being there at key moments when work is handed off. We listed 7 phases of product development and not coincidentally there are 6 key handoffs that need to happen for a project to go smoothly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzd8j4fiycsp8nlol9u52.png" class="article-body-image-wrapper"&gt;&lt;img alt="Illustration of campfire in rocky night landscape" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzd8j4fiycsp8nlol9u52.png" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. User Story Workshop
&lt;/h3&gt;

&lt;p&gt;When: The designer is picking up the project&lt;/p&gt;

&lt;p&gt;Who: PM, Designer, Epic Owner&lt;/p&gt;

&lt;p&gt;The designer's job is to get into the weeds of &lt;em&gt;how&lt;/em&gt; a problem will be solved for a user. But before Engineering can start implementing a design, it's crucial that the team is aligned on &lt;em&gt;what&lt;/em&gt; problem is being solved.&lt;/p&gt;

&lt;p&gt;User Story Workshop Checklist:&lt;br&gt;
☐ Distill project brief into concrete user stories (as a [persona] I want [scenario] so that [motivation])&lt;br&gt;
☐ Propose simplifications&lt;br&gt;
☐ Separate stories into P0 (hard requirements) and P1, P2, etc&lt;/p&gt;

&lt;p&gt;Engineers can provide huge value at this stage, and get massive value in return:&lt;/p&gt;

&lt;p&gt;✨ Engineers can propose simplifications of a concept, to avoid wasting Design time mocking up features that don't need to be built.&lt;/p&gt;

&lt;p&gt;✨ Engineers can use stories later on to resolve ambiguities in the mockups, and to help decide between tradeoffs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnl4m5sjai62ygc73bfz.png" class="article-body-image-wrapper"&gt;&lt;img alt="Illustration of 3D house blueprint with tiny architects outside the house" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnl4m5sjai62ygc73bfz.png" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Design Review
&lt;/h3&gt;

&lt;p&gt;When: A new set of designs is ready for feedback&lt;/p&gt;

&lt;p&gt;Who: Designer, Epic Owner&lt;/p&gt;

&lt;p&gt;Most engineers pride themselves on being able to build anything that is specced out for them. But Design Review is one of the places engineers ever create the most "value per hour":&lt;/p&gt;

&lt;p&gt;Design Review Checklist:&lt;br&gt;
☐ Point out any gaps in the flows that the designer may have missed&lt;br&gt;
☐ Propose simplifications&lt;/p&gt;

&lt;p&gt;The value engineers get from Design Review...&lt;/p&gt;

&lt;p&gt;✨ Engineers save their own time avoiding building unnecessary features&lt;/p&gt;

&lt;p&gt;✨ Engineers make sure the designs represent the full scope of work, so projects don't slip later on due to "scope creep" &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwsfkyglcv00k0702axu.png" class="article-body-image-wrapper"&gt;&lt;img alt="Illustration of man on ladder adding checkboxes in the sky" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwsfkyglcv00k0702axu.png" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Task-out
&lt;/h3&gt;

&lt;p&gt;When: Before development starts or a firm estimate is needed&lt;/p&gt;

&lt;p&gt;Who: Epic Owner&lt;/p&gt;

&lt;p&gt;Task-out means taking all of the User Stories, specifications, and mockups and planning out the sequence of tasks that need to be done for the feature to be delivered.&lt;/p&gt;

&lt;p&gt;Task-out checklist:&lt;br&gt;
☐ Break work into bite-sized tasks&lt;br&gt;
☐ Ensure tickets have acceptance criteria&lt;br&gt;
☐ Indicate which tickets are blocked by others&lt;br&gt;
☐ Add priorities to tickets (blocker/high/low)&lt;br&gt;
☐ Document questions to be resolved &amp;amp; decisions made&lt;br&gt;
☐ Schedule additional design review if needed&lt;/p&gt;

&lt;p&gt;Task-out provides...&lt;/p&gt;

&lt;p&gt;✨ A way for Engineers to make reliable estimates. The simplest thing you can do is divide the number of stories by how many stories a dev typically does per week and get a pretty reasonable number of dev weeks to allocate.&lt;/p&gt;

&lt;p&gt;✨ Proper sequencing of tasks avoids bottlenecks that cause delays and make Engineers look bad.&lt;/p&gt;

&lt;p&gt;✨ Writing acceptance criteria often surfaces some remaining open questions. Answering those questions before development means Engineers avoid throwing away work halfway through development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0n348ofdhjelo9mdpke.png" class="article-body-image-wrapper"&gt;&lt;img alt="Illustration of job site with cranes" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0n348ofdhjelo9mdpke.png" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Kickoff
&lt;/h3&gt;

&lt;p&gt;When: Engineers are ready to start development&lt;/p&gt;

&lt;p&gt;Who: Epic Owner, Designer, implementation team&lt;/p&gt;

&lt;p&gt;Kickoff is when you bring the implementation team into the fold and start actually building.&lt;/p&gt;

&lt;p&gt;☐ Walk through designs&lt;br&gt;
☐ Walk through task list and point out which work is out of scope for each task&lt;br&gt;
☐ Talk through who will do what&lt;br&gt;
☐ Move stories onto the board or into an iteration&lt;br&gt;
☐ Assign a start and end date for project&lt;br&gt;
☐ If questions come up, schedule additional design review&lt;/p&gt;

&lt;p&gt;Value for Engineers:&lt;/p&gt;

&lt;p&gt;✨ Avoid having engineers go down rabbit holes trying to do too many tasks at once.&lt;/p&gt;

&lt;p&gt;✨ Make sure everyone understands the full scope of work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzuumkvjk03jrqzk0sac.png" class="article-body-image-wrapper"&gt;&lt;img alt="Illustration of a road forked amongst yellow autumn trees in the mountains" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzuumkvjk03jrqzk0sac.png" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Technical Design Discussion
&lt;/h3&gt;

&lt;p&gt;When: Substantial questions pop up as to how to implement something&lt;/p&gt;

&lt;p&gt;Who: Implementation team, Epic Owner&lt;/p&gt;

&lt;p&gt;Once development has begun, one of the best things an Epic Owner can do is to pull the team together to resolve questions that come up.&lt;/p&gt;

&lt;p&gt;Typically, there will be a specific dev who is charged with implementing something that could be done in a few different ways.&lt;/p&gt;

&lt;p&gt;As Epic Owner, you should ask that dev to start a document with a brief overview of the problem and possible solutions.&lt;/p&gt;

&lt;p&gt;Technical Design Discussion Checklist:&lt;br&gt;
☐ Document open questions&lt;br&gt;
☐ Document possible answers to those questions&lt;br&gt;
☐ Document pros and cons for each possible answer&lt;br&gt;
☐ Document decisions made&lt;br&gt;
☐ Surface any new scope that has been added to the PM&lt;/p&gt;

&lt;p&gt;Value for Engineers:&lt;/p&gt;

&lt;p&gt;✨ Save time finding better, easier solutions to challenges.&lt;/p&gt;

&lt;p&gt;✨ Avoid having the same discussions over and over again (decisions are documented!)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyq3w38ax1l6rby5ubeja.png" class="article-body-image-wrapper"&gt;&lt;img alt="Chef putting final garnish on a dish" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyq3w38ax1l6rby5ubeja.png" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. QA handoff/Bug Bash
&lt;/h3&gt;

&lt;p&gt;When: A project is ready for QA&lt;/p&gt;

&lt;p&gt;Who: Epic Owner, QA lead, PM&lt;/p&gt;

&lt;p&gt;Finally, it's time to get a project out to your users, so we need to ensure quality. Having a &lt;a href="https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8"&gt;Bug Warden&lt;/a&gt; will help, but as Epic Owner you also need to facilitate QA.&lt;/p&gt;

&lt;p&gt;Your job as Epic Owner is to make sure important details about the changes get to the people who will be doing QA and onboarding customers.&lt;/p&gt;

&lt;p&gt;This is also a great time to bring the PM back into the fold, when there's time to allocate final project resources and make final adjustments to scope, quality bars, etc.&lt;/p&gt;

&lt;p&gt;QA Handoff Checklist:&lt;br&gt;
☐ List new behavior that is in the release&lt;br&gt;
☐ Note refactors that were done and what areas of the product they might affect&lt;br&gt;
☐ List known issues&lt;/p&gt;

&lt;p&gt;Value for Engineers:&lt;/p&gt;

&lt;p&gt;✨ A good list of known issues means fewer duplicate bug reports the team has to triage&lt;/p&gt;

&lt;p&gt;✨ If QA knows the full scope of changes they'll find more bugs for you sooner.&lt;/p&gt;

&lt;p&gt;✨ Better QA means fewer embarrassing bug reports coming from customers, product demos, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick, focused meetings
&lt;/h2&gt;

&lt;p&gt;That's a lot of meetings! (you might be thinking)&lt;/p&gt;

&lt;p&gt;And it is. But in my experience you ignore these key handoffs at your own peril. Every one of these checklists comes from actual projects that went off the rails.&lt;/p&gt;

&lt;p&gt;But it's also important to remember that meetings don't have to be heavy or even lengthy.&lt;/p&gt;

&lt;p&gt;To keep meetings light...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invite as few people as possible&lt;/strong&gt;—Try to keep out people who are "just going to listen." These meetings are for specific people to answer questions so they can get work done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the checklists&lt;/strong&gt;—I've given you a ready-made agenda for each of these handoffs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cut people off&lt;/strong&gt;—If folks start discussing something that's not on the agenda, praise them for bringing up something important and tell them to put their own time on the calendar to work out the details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finish early&lt;/strong&gt;—You might have 50 minutes on the calendar, but if you can let everyone go in 15, do it! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete process&lt;/strong&gt;—If a meeting is not feeling &lt;em&gt;critical&lt;/em&gt; for your team, consider removing it. And remove items from the checklists that don't feel essential too.&lt;/p&gt;

&lt;p&gt;The goal is for these meetings to feel like like no brainers to everyone involved. I've laid out what I think is are some good starting points, but for your team to really buy in to this kind of structure, they need to feel like they are continuing to author the process.&lt;/p&gt;

&lt;p&gt;It's better to do fewer of these meetings, but have your team really bought into the ones you do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be patient
&lt;/h2&gt;

&lt;p&gt;Lastly, don't worry if you can't do all of these things right away.&lt;/p&gt;

&lt;p&gt;The best time to introduce new process is when your team is struggling with a specific challenge.&lt;/p&gt;

&lt;p&gt;Is your team struggling with buggy releases? Add QA Handoffs. Do you find your timelines keep slipping? Start doing more thorough Task-outs.&lt;/p&gt;

&lt;p&gt;Rather than feeling like you're putting onerous process on people, it will feel like you're saving the day.&lt;/p&gt;




&lt;p&gt;If it's still January 2025, and you are looking for an experienced Engineering Manager in San Francisco or remote... &lt;strong&gt;&lt;a href="https://snowedin.net/" rel="noopener noreferrer"&gt;Hire me!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Part of a multi-part part series on "Software Engineering Roles"...
&lt;/h3&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/erikpukinskis" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/erikpuk" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; to hear when the next part is posted!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/erikpuk/four-roles-your-software-team-cant-function-without-2b5f"&gt;Four roles your software team can't function without&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8"&gt;If you have bugs, you need a Bug Warden&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/erikpuk/how-to-own-a-project-if-youre-an-engineer-12d5"&gt;How to "own" a project if you're an engineer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 4: The Release Warden&lt;/li&gt;
&lt;li&gt;Part 5: The Program Manager&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Cover image © &lt;a href="https://www.dreamstime.com/people-characters-working-as-well-coordinated-mechanism-engaged-teamwork-vector-illustration-young-diligent-man-woman-image335036916" rel="noopener noreferrer"&gt;Evgenii Naumov&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;User story workshop illustration © &lt;a href="https://www.dreamstime.com/campfire-woods-cartoon-environment-adventure-game-forest-scene-outdoor-exploration-illustration-nighttime-camping-campfire-image329604835" rel="noopener noreferrer"&gt;Ladadikart&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Design review illustration © &lt;a href="https://www.dreamstime.com/stock-illustration-architectural-design-blueprint-drawing-d-isometric-illustration-project-construction-house-vector-plan-home-image58113047" rel="noopener noreferrer"&gt;Burlesck&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Task out illustration © &lt;a href="https://www.dreamstime.com/criteria-checklist-report-evaluation-list-complete-checkbox-finish-work-to-do-task-done-quality-assurance-control-checkmark-image349426452" rel="noopener noreferrer"&gt;Eamesbot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kickoff illustration © &lt;a href="https://www.dreamstime.com/construction-site-scene-heavy-machinery-color-illustration-vector-image337583571" rel="noopener noreferrer"&gt;Applikbeats777&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Technical design discussion Illustration © &lt;a href="https://www.dreamstime.com/fork-road-forest-autumn-trees-vector-illustration-fork-road-forest-autumn-trees-image159476935" rel="noopener noreferrer"&gt;Rogerothornhill&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;QA handoff/Bug Bash illustration © &lt;a href="https://www.dreamstime.com/chef-cook-adding-condiment-to-restaurant-dish-professional-cooking-preparing-meal-decorating-food-seasoning-haute-cuisine-image241619998" rel="noopener noreferrer"&gt;Ernest Akayeu&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>planning</category>
      <category>roadmap</category>
    </item>
    <item>
      <title>If you have bugs, you need a Bug Warden</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Mon, 20 Jan 2025 18:02:52 +0000</pubDate>
      <link>https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8</link>
      <guid>https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8</guid>
      <description>&lt;p&gt;Last time, I introduced the &lt;a href="https://dev.to/erikpuk/four-roles-your-software-team-cant-function-without-2b5f"&gt;Four roles your software team can't function without&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These roles are the four areas where I think software engineers need to go beyond just writing code and take on some broader responsibilities.&lt;/p&gt;

&lt;p&gt;One of the busiest of these is a rotating role called the "Bug Warden".&lt;/p&gt;

&lt;h1&gt;
  
  
  What does a Bug Warden do?
&lt;/h1&gt;

&lt;p&gt;The Bug Warden's core responsibility is &lt;strong&gt;triaging bug reports&lt;/strong&gt;. This doesn't &lt;em&gt;need&lt;/em&gt; to be a heavy, time consuming task. It can be as simple as delegating the investigation to another engineer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6x23rrrysl04396rqr96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6x23rrrysl04396rqr96.png" alt="Image description" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However in practice there are a number of things the Bug Warden might need to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask the reporter for more details&lt;/li&gt;
&lt;li&gt;Create a ticket in a project tracker&lt;/li&gt;
&lt;li&gt;Try to reproduce the issue&lt;/li&gt;
&lt;li&gt;Find a duplicate ticket, for issues that were previously reported&lt;/li&gt;
&lt;li&gt;Figure out which engineer should be assigned the bug, assign them, and @-mention them so they're aware of it&lt;/li&gt;
&lt;li&gt;Assign a Severity, a Release Status, and any other metadata your company is tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;em&gt;critical&lt;/em&gt; work which often falls through the cracks on teams, with Engineering assuming QA will do it, and QA assuming Product will do it, and Product assuming Engineering will do it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfvqtv5dw4tsf398c9lp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfvqtv5dw4tsf398c9lp.png" alt="Image description" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What you need to track
&lt;/h1&gt;

&lt;p&gt;There are a million ways to organize your bug reports, but in my experience there are two pieces of information that are critical to track:&lt;/p&gt;

&lt;p&gt;1) Severity — A &lt;code&gt;S0&lt;/code&gt; bug should be worked on immediately, an &lt;code&gt;S1&lt;/code&gt; bug should be worked on in this release cycle, and an &lt;code&gt;S3&lt;/code&gt; can wait for the next cycle.&lt;/p&gt;

&lt;p&gt;2) Release Status — Fixes often move through several environments on their way to production. So this status will either be the target environment (for a known issue) or the release environment (for issues that have been fixed):&lt;br&gt;
    - &lt;code&gt;Fix in an upcoming release (dev)&lt;/code&gt;&lt;br&gt;
    - &lt;code&gt;Fix in the current release (staging)&lt;/code&gt;&lt;br&gt;
    - &lt;code&gt;Hotfix (production)&lt;/code&gt;&lt;br&gt;
    - &lt;code&gt;Fixed on dev&lt;/code&gt;&lt;br&gt;
    - &lt;code&gt;Fixed on staging&lt;/code&gt;&lt;br&gt;
    - &lt;code&gt;Fixed on prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These two pieces of information allow the Bug Warden and other stakeholders to make a variety critical decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Should an engineer stop what they're doing and focus on a bug?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Does a release need to be delayed?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are there fixes that needs to merged into the right environment?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an &lt;code&gt;S0&lt;/code&gt; was a hotfix that went out to production, it may need to be back-merged to staging for further integration. If an S1 has been tested in the development environment, it may need to be pushed to staging.&lt;/p&gt;

&lt;p&gt;These decisions can only be made correctly if you track the status!&lt;/p&gt;

&lt;h1&gt;
  
  
  Beyond bug reports
&lt;/h1&gt;

&lt;p&gt;In addition to the core job of triage, the Bug Warden is also responsible for running three meetings each cycle:&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug Warden Handoff (near the start of the release cycle)
&lt;/h3&gt;

&lt;p&gt;The primary purpose of this meeting is to there are no lapses in coverage; that when your rotation is done another engineer picks up the baton.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invitees:&lt;/strong&gt; The incoming Bug Warden, and the outgoing Bug Warden&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checklist:&lt;/strong&gt;&lt;br&gt;
☐ Add the new Warden to a &lt;strong&gt;@bug-warden&lt;/strong&gt; team handle in Slack, and remove yourself&lt;br&gt;
☐ Make sure the new Warden is invited to the Cherry Pick and Retro&lt;br&gt;
☐ Invite the &lt;em&gt;next&lt;/em&gt; Warden to the next handoff&lt;br&gt;
☐ Review any &lt;code&gt;S0&lt;/code&gt; or &lt;code&gt;S1&lt;/code&gt; bugs that might affect the next release&lt;br&gt;
☐ Note any outstanding bug reports that still need to be triaged&lt;/p&gt;

&lt;h3&gt;
  
  
  Bugfix Cherry Pick (towards the end of the release cycle)
&lt;/h3&gt;

&lt;p&gt;This meeting is a last chance opportunity to reallocate resources to fixing critical bugs before a release goes out. A product owner is invited so they can help the engineers understand what features are critical to ship in this release, and what issues are critical to fix before they can ship.&lt;/p&gt;

&lt;p&gt;Often this is just a rubber stamp, but sometimes there is a critical bug that is blocking a release and at this meeting the team can raise a flag if more resources are needed to squash it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invitees:&lt;/strong&gt; Bug Warden, Release Warden, and a product owner&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checklist:&lt;/strong&gt; &lt;br&gt;
☐ Quickly review any un-triaged bug reports &lt;br&gt;
☐ Make sure all issues that need to be &lt;code&gt;S1&lt;/code&gt; are tagged as &lt;code&gt;S1&lt;/code&gt; and &lt;code&gt;Fix in the current release&lt;/code&gt;&lt;br&gt;
☐ Review all &lt;code&gt;S1&lt;/code&gt; issues to check if they have owners + will be ready for the release&lt;br&gt;
☐ If the release is at risk, post in #releases-and-testing to let the team know what's going on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug Retro (after a release cycle)
&lt;/h3&gt;

&lt;p&gt;The last thing the Bug Warden will do is run a retrospective on some of the issues that came up. You figure out what needs to improve in your release process, testing, tooling, bug reporting, and triage.&lt;/p&gt;

&lt;p&gt;This is a crucial feedback loop that drives your software quality up to where it needs to be. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invitees:&lt;/strong&gt; The Bug Warden, at least one engineer, a representative of the customer, and a product owner&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checklist:&lt;/strong&gt;&lt;br&gt;
☐ Choose a couple bugfixes from the release to retro&lt;br&gt;
☐ Run through &lt;a href="https://en.wikipedia.org/wiki/Five_whys" rel="noopener noreferrer"&gt;"5 why's"&lt;/a&gt;&lt;br&gt;
☐ Note any action items&lt;/p&gt;

&lt;h1&gt;
  
  
  The Warden is busy
&lt;/h1&gt;

&lt;p&gt;Although these handful of meetings might only amount to an hour or two per week, the actual bug triage process can take up a LOT of time for the engineer who has that responsibility.&lt;/p&gt;

&lt;p&gt;Because of that, we always want to reassure the Bug Warden that their other development work can take a back seat while they occupy this critical role.&lt;/p&gt;

&lt;p&gt;And it also means we don't want to assign someone who is under the gun to ship a critical feature as the Bug Warden. If someone is up for the job, and there is higher priority work for them to do, they should swap with someone else!&lt;/p&gt;

&lt;h1&gt;
  
  
  We can relax
&lt;/h1&gt;

&lt;p&gt;Although it's real work for the engineer doing this job, the real benefit is that everyone can relax because they know someone is responsible for handling bugs.&lt;/p&gt;

&lt;p&gt;When they're busy, the other engineers can safely ignore the #bug-reports channel knowing someone will ping them if they're needed.&lt;/p&gt;

&lt;p&gt;Management can rest easy knowing that incidents are being investigated and the stability and reliability of the product is getting better every week.&lt;/p&gt;

&lt;p&gt;Stakeholders from across your team know exactly where they can fire off a critical bug report to get immediate attention. And they know where to put more minor bug reports without causing a distraction.&lt;/p&gt;

&lt;p&gt;In other words: we can all relax knowing the product is solid.&lt;/p&gt;




&lt;p&gt;If it's still January 2025, and you are looking for an experienced Engineering Manager in San Francisco or remote... &lt;strong&gt;&lt;a href="https://snowedin.net/" rel="noopener noreferrer"&gt;Hire me!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Part of a multi-part part series on "Software Engineering Roles"...
&lt;/h3&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/erikpukinskis" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/erikpuk" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; to hear when the next part is posted!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/erikpuk/four-roles-your-software-team-cant-function-without-2b5f"&gt;Four roles your software team can't function without&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8"&gt;If you have bugs, you need a Bug Warden&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/erikpuk/how-to-own-a-project-if-youre-an-engineer-12d5"&gt;How to "own" a project if you're an engineer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 4: The Release Warden&lt;/li&gt;
&lt;li&gt;Part 5: The Program Manager&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Cover image ID &lt;a href="https://www.dreamstime.com/print-image337353589" rel="noopener noreferrer"&gt;337353589&lt;/a&gt; © &lt;br&gt;
&lt;a href="https://www.dreamstime.com/minirhenyx_info" rel="noopener noreferrer"&gt;Minirhenyx&lt;/a&gt; | &lt;a href="https://www.dreamstime.com/stock-photos" rel="noopener noreferrer"&gt;Dreamstime.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>testing</category>
      <category>sre</category>
      <category>management</category>
    </item>
    <item>
      <title>Four Roles Your Software Team Can't Function Without</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Fri, 13 Dec 2024 18:37:31 +0000</pubDate>
      <link>https://dev.to/erikpuk/four-roles-your-software-team-cant-function-without-2b5f</link>
      <guid>https://dev.to/erikpuk/four-roles-your-software-team-cant-function-without-2b5f</guid>
      <description>&lt;p&gt;We've all seen it go both ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Company A:&lt;/strong&gt; Has no real structure in place, just a bunch of stuff that tends to happen because individuals make it happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Company B:&lt;/strong&gt; Documents structure around everything. Everyone has strictly defined responsibilities. Doing anything across multiple roles requires navigating a byzantine maze of personalities and processes. Because of that, people just focus on their job description and block everything else out.&lt;/p&gt;

&lt;p&gt;One of the pieces of feedback I got at &lt;a href="https://www.spaero.bio/" rel="noopener noreferrer"&gt;Spaero Bio&lt;/a&gt; made me incredibly proud. A dev told me: &lt;em&gt;this is the first company I've worked at that struck the right balance between process and freedom&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So let's talk about the &lt;strong&gt;structure that a software engineering team can't do without&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solid engineering, every time
&lt;/h2&gt;

&lt;p&gt;When you look closely at the key contributors at companies in that "Company A" category, you will see passionate people taking responsibility for critical work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An engineer who was driven to drive out bugs&lt;/li&gt;
&lt;li&gt;A tech lead who made sure that estimations were done reliably&lt;/li&gt;
&lt;li&gt;A designer who sought out blunt feedback from engineers, business, and sales people&lt;/li&gt;
&lt;li&gt;A PM that diligently tracks progress against key product milestones&lt;/li&gt;
&lt;li&gt;And so-on...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But in the end, you can't clone these people. And statistically, any given team will be missing some of these passions. All the key skills are represented in your organization, and yet these critical work pieces of "side work" are not done consistently.&lt;/p&gt;

&lt;p&gt;I have found that the way to turn a team of productive, passionate engineers into a &lt;strong&gt;steady, trusted unit that the entire organization can rely on&lt;/strong&gt;, is to introduce rotating roles that go beyond the core engineering responsibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Roles" = people know what today's job is
&lt;/h2&gt;

&lt;p&gt;The foundational role on an engineering team is of course "Engineer". Every engineer knows the basics of what this job entails: You grab some kind of spec, you write the code, and you deploy it.&lt;/p&gt;

&lt;p&gt;But left to their own devices, software engineers will run into a number of classic roadblocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A smart engineer spends lots of time building beautiful code that wasn't necessary in the first place.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Trickle requirements" cause expensive rebuilds of the same features over and over again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bug reports pile up with Product thinking Engineering will handle it and Engineering thinking Product will handle it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Releases are going out but the sales team never knows when a feature will go live.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Embarrassing bugs appear during a key demo or customer onboarding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Projects have "two weeks left"... for two months.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And most of us know some ways to avoid these problems. It's not that we &lt;em&gt;want&lt;/em&gt; these things to happen.&lt;/p&gt;

&lt;p&gt;The challenge is the typical engineer has so many demands on their time, &lt;strong&gt;it's impossible for them to do everything at once&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's where my four roles come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four roles
&lt;/h2&gt;

&lt;p&gt;👉 If you know you're taking the &lt;strong&gt;Release Warden&lt;/strong&gt; role for the next release, you know to focus on the work that is wrapping up, not upcoming work.&lt;/p&gt;

&lt;p&gt;👉 If you are the &lt;strong&gt;&lt;a href="https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8"&gt;Epic Owner&lt;/a&gt;&lt;/strong&gt; for an epic that's about to have a kickoff next week, you know to focus on planning that work, not fixing bugs.&lt;/p&gt;

&lt;p&gt;👉 If you're &lt;strong&gt;&lt;a href="https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8"&gt;Bug Warden&lt;/a&gt;&lt;/strong&gt; for the current release, you know that triaging bug reports quickly takes precedence over finishing that new feature.&lt;/p&gt;

&lt;p&gt;👉 If you're the &lt;strong&gt;Program Manager&lt;/strong&gt; you know that shipping is not your first priority. You're make sure information is flowing between stakeholders so the best possible decisions are getting made.&lt;/p&gt;

&lt;p&gt;... and if you are not in any of those roles this week, you know that it's the perfect time to be heads down on a project, cranking out that sweet, sweet engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roles the rest of the organization can trust
&lt;/h2&gt;

&lt;p&gt;I won't pretend these came out of a principled analysis—they didn't.&lt;/p&gt;

&lt;p&gt;But they did come out of a decade or two of trial and error... Seeing projects get jammed up, adding process to smooth things out, removing process that was being skipped, refining responsibilities in response to retrospectives, and so-on. &lt;/p&gt;

&lt;p&gt;And in hindsight, I think this kind of heavier process works best near &lt;strong&gt;key inputs and outputs between Engineering and other parts of the organization:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inputs to Engineering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bug reports (from QA)&lt;/li&gt;
&lt;li&gt;Product specifications (from PMs)&lt;/li&gt;
&lt;li&gt;Designs (from designers)&lt;/li&gt;
&lt;li&gt;Business priorities (from PMs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Outputs of Engineering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New features (for QA, Sales)&lt;/li&gt;
&lt;li&gt;Bug fixes (for QA)&lt;/li&gt;
&lt;li&gt;Roadmaps (for PMs)&lt;/li&gt;
&lt;li&gt;Status updates (for Management)&lt;/li&gt;
&lt;li&gt;Design feedback (for Designers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are moments where some information has been generated that is either going to inform the engineering work, or will inform the decisions the rest of the organization is making around that work.&lt;/p&gt;

&lt;h2&gt;
  
  
  As few roles as possible, as little process as could work
&lt;/h2&gt;

&lt;p&gt;One final note, on taking in too far...&lt;/p&gt;

&lt;p&gt;It can be tempting to just document "all the roles!" and give people a checklist for every situation. But there's a very specific reason why this post is &lt;em&gt;four&lt;/em&gt; roles your team can't function without and not &lt;em&gt;ten&lt;/em&gt; roles.&lt;/p&gt;

&lt;p&gt;If you introduce too much process, it starts to feel very heavy. People start skipping steps, and you lose the whole "trusted unit that the entire organization can rely on" thing.&lt;/p&gt;

&lt;p&gt;If you're a senior engineer, or an engineering mananger, or a product manager who is trying to introduce this kind of process to your team, you can only introduce &lt;strong&gt;one process step at a time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Your team needs time to digest these processes. To integrate them into their existing habits. And to begin to trust that 1) they're really necessary, and 2) they're really going to be happening from now on.&lt;/p&gt;

&lt;p&gt;There &lt;em&gt;will&lt;/em&gt; be areas where your team is limping along, and you know how to fix it, and you don't. Because you are still building trust somewhere else in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's do the work
&lt;/h2&gt;

&lt;p&gt;We can talk all day about roles and responsibilities in the abstract. But these are &lt;em&gt;production-oriented roles&lt;/em&gt; with very specific things for an engineer to do, and specific things they won't do.&lt;/p&gt;

&lt;p&gt;So in the next series of posts, I'll dive into each one of these roles in detail: The decisions that each role gets to make, the meetings that need to happen, the communication that needs to occur.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/erikpukinskis/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/erikpuk" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; to hear when the next part is posted!&lt;/p&gt;




&lt;p&gt;If it's still January 2025, and you are looking for an experienced Engineering Manager in San Francisco or remote... &lt;strong&gt;&lt;a href="https://snowedin.net/" rel="noopener noreferrer"&gt;Hire me!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Part of a multi-part part series on "Software Engineering Roles"...
&lt;/h3&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/erikpukinskis" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/erikpuk" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; to hear when the next part is posted!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/erikpuk/four-roles-your-software-team-cant-function-without-2b5f"&gt;Four roles your software team can't function without&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/erikpuk/if-you-have-bugs-you-need-a-bug-warden-49g8"&gt;If you have bugs, you need a Bug Warden&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/erikpuk/how-to-own-a-project-if-youre-an-engineer-12d5"&gt;How to "own" a project if you're an engineer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 4: The Release Warden&lt;/li&gt;
&lt;li&gt;Part 5: The Program Manager&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Cover image copyright © &lt;a href="https://www.dreamstime.com/ominaesi_info" rel="noopener noreferrer"&gt;Ominaesi&lt;/a&gt; | &lt;a href="https://www.dreamstime.com/" rel="noopener noreferrer"&gt;Dreamstime.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>productivity</category>
      <category>startup</category>
      <category>management</category>
    </item>
    <item>
      <title>What is a Pro Web Engineer?</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Tue, 13 Aug 2024 16:00:25 +0000</pubDate>
      <link>https://dev.to/erikpuk/what-is-a-pro-web-engineer-2ap9</link>
      <guid>https://dev.to/erikpuk/what-is-a-pro-web-engineer-2ap9</guid>
      <description>&lt;p&gt;Terms like "professional" and "engineer" are bandied about in the software world. A lot of us are engineers by title, and most of us would like to think of ourselves as professional.&lt;/p&gt;

&lt;p&gt;But what do these two words really mean?&lt;/p&gt;

&lt;p&gt;I think the answer to that question can help you understand how to proceed in lots of tricky work situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should I be cutting corners on the quality of my work?&lt;/li&gt;
&lt;li&gt;Is it OK to express strong emotions in the workplace?&lt;/li&gt;
&lt;li&gt;When can I get my way?&lt;/li&gt;
&lt;li&gt;When do I compromise?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This blog will lay out what it means to be both "professional" and an "engineer" for those of us building software for the web.&lt;/p&gt;

&lt;p&gt;We'll go into detail on each of these, but at a high level here's what I think makes for a professional web engineer...&lt;/p&gt;

&lt;p&gt;An engineer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Knows the materials&lt;/strong&gt; — You understand the properties of different materials, tools, and patterns, and can choose options which are fit to purpose.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documents the decisions&lt;/strong&gt; — Numbers are crunched and shared, reasons for decisions are made clear. Available tradeoffs are presented.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Works within a code of ethics&lt;/strong&gt; — You don't design a bridge you think is dangerous.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A professional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Is &lt;strong&gt;efficient&lt;/strong&gt; — You don't waste your time, and you don't waste other peoples' time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is &lt;strong&gt;accountable&lt;/strong&gt; — When you screw up, you make it right.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Works well with different kinds of people&lt;/strong&gt; — You don't just have one specific kind of person you can work with. You do the work to allow folks from many backgrounds to be comfortable working with you.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The web:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is whatever software you can deliver in a web browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow &lt;a href="https://twitter.com/erikpuk" rel="noopener noreferrer"&gt;@erikpuk&lt;/a&gt; on Twitter to follow as we learn how each of these goals plays out in practice.&lt;/p&gt;

&lt;p&gt;Image credit: &lt;a href="https://www.flickr.com/photos/davelauretti/" rel="noopener noreferrer"&gt;Dave Lauretti&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>engineering</category>
      <category>professionalism</category>
    </item>
    <item>
      <title>Big, scary, and ignored: Are backlogs even worth it?</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Fri, 15 Sep 2023 22:28:12 +0000</pubDate>
      <link>https://dev.to/erikpuk/backlog-44ca</link>
      <guid>https://dev.to/erikpuk/backlog-44ca</guid>
      <description>&lt;p&gt;Let's start with some observations about backlogs:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Backlogs Want To Be Scary
&lt;/h3&gt;

&lt;p&gt;Maybe we'll call that the "Halloween Law of Backlogs". A backlog's favorite day of the year is Halloween.&lt;/p&gt;

&lt;p&gt;To understand why, let's think about the reasons you'd put something into a backlog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's not strictly necessary to hit your immediate goals&lt;/li&gt;
&lt;li&gt;You're not sure how it should be designed&lt;/li&gt;
&lt;li&gt;It's a "big bite" and not quite ready to be a Story&lt;/li&gt;
&lt;li&gt;It's been requested by one user, and you're not sure how broadly useful it is&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of those things are scary.&lt;/p&gt;

&lt;p&gt;On the flip side of that, there are some tasks which are not scary at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's clearly necessary&lt;/li&gt;
&lt;li&gt;You know how to design it&lt;/li&gt;
&lt;li&gt;It's a "small bite"&lt;/li&gt;
&lt;li&gt;It's going to be useful to many users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those kinds of tasks don't get dumped in a backlog, they go into production. Everyone is aligned on those kinds of tasks.&lt;/p&gt;

&lt;p&gt;So a backlog is made of at least semi-scary items. But one additional factor makes backlogs very scary:&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Backlogs Want To Be BIG
&lt;/h3&gt;

&lt;p&gt;It's a lot faster to add something to a backlog than it is to remove it.&lt;/p&gt;

&lt;p&gt;Removing it requires either A) deleting it, which might be disappointing to a user, disappointing to one of your colleagues, or disappointing to you. Deleting backlog items often requires some conversation and hand-wringing.&lt;/p&gt;

&lt;p&gt;Or, B) moving the item into production. That requires overcoming the scary reasons the item was put in the backlog in the first place, and marshaling resources to get it done.&lt;/p&gt;

&lt;p&gt;So backlogs want grow and grow.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Backlogs Want To Be Ignored
&lt;/h3&gt;

&lt;p&gt;By design, they are where tasks go to avoid distracting the team. They live in a corner of your workspace that few people visit. It's not a Kanban Board that people are seeing every day. It's not on a Google Calendar, or in a list of Pull Requests.&lt;/p&gt;

&lt;p&gt;Backlogs want to have as few eyes on them as possible. That's their job.&lt;/p&gt;

&lt;h3&gt;
  
  
  The big, scary, important thing you've been ignoring
&lt;/h3&gt;

&lt;p&gt;And yet, we wouldn't put things in a backlog if they weren't &lt;em&gt;important&lt;/em&gt;. But that just makes backlogs even scarier.&lt;/p&gt;

&lt;p&gt;The big, scary, important thing we've all been ignoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are backlogs useful for
&lt;/h3&gt;

&lt;p&gt;It's worthwhile considering how a backlog can function in the product development pipeline.&lt;/p&gt;

&lt;p&gt;In Scrum, the place a backlog would be used is for &lt;a href="https://gist.github.com/erikpukinskis/a8a61b8fbd19f8063737f7d7199dc2b1#create-project-vision" rel="noopener noreferrer"&gt;CREATE PROJECT VISION&lt;/a&gt;. That's the place where you are considering all the potential projects you might invest in, looking at the business case, marketing, costs and profits, etc.&lt;/p&gt;

&lt;p&gt;That points us at some interesting criteria for what makes a good backlog:&lt;/p&gt;

&lt;h3&gt;
  
  
  What makes a good backlog
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Ideal backlog items address the business case, marketing, and costs and profits behind the work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are the things you'll be thinking about when you are reviewing your backlog so they should be the things you document when deciding an item should go in there.&lt;/p&gt;

&lt;p&gt;If any of these concerns are obvious misses, don't put it in the backlog.&lt;/p&gt;

&lt;p&gt;And speaking of not putting things in the backlog...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Don't put things in the backlog you won't do soon&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since &lt;em&gt;Backlogs Want To Be BIG&lt;/em&gt;, and a big backlog is more difficult to review, it's important to find ways to keep your backlog small.&lt;/p&gt;

&lt;p&gt;An easy rule is "If you don't think you'll get to it this quarter or next, don't put it in the backlog".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Delete backlog items mercilessly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Understand that it's going to hurt. If it was truly unimportant, it never would've landed in your backlog in the first place. So know that you're going to be deleting important things out of your backlog.&lt;/p&gt;

&lt;p&gt;One thing to keep in mind which can make this easier is...&lt;/p&gt;

&lt;h3&gt;
  
  
  The Boomerang Rule
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Important things will come back to you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If something is truly a broad concern for many of your colleagues, or many of your users, someone will mention it again.&lt;/p&gt;

&lt;p&gt;And eventually someone will mention it in a less scary time, when&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's clearer how necessary it is&lt;/li&gt;
&lt;li&gt;You have a better sense of how design it&lt;/li&gt;
&lt;li&gt;You can see a "smaller bite" to take&lt;/li&gt;
&lt;li&gt;More users have made it clear they need it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, as important as it is today, remember that the important stuff will only get importanter.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Small, Helpful, Important Thing You Keep Tabs On
&lt;/h3&gt;

&lt;p&gt;With just those few small changes to your workflow, your backlog can transform from the Big, Scary, Important Thing You've Been Ignoring to something you actually &lt;em&gt;want&lt;/em&gt; to keep tabs on.&lt;/p&gt;

&lt;p&gt;If you are careful about adding things, brazen about removing them, and stay focused on backlog items that contribute to your project vision, hopefully your backlog will be something folks want to spend time in.&lt;/p&gt;

</description>
      <category>planning</category>
      <category>scrum</category>
      <category>backlogs</category>
      <category>process</category>
    </item>
    <item>
      <title>The Persistent Test Factory Pattern</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Thu, 20 Jul 2023 04:06:58 +0000</pubDate>
      <link>https://dev.to/erikpuk/the-persistent-test-factory-pattern-5mg</link>
      <guid>https://dev.to/erikpuk/the-persistent-test-factory-pattern-5mg</guid>
      <description>&lt;p&gt;I've worked on many codebases that used what I consider to be one of the worst patterns in automated test architecture:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fixtures.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yuck.&lt;/p&gt;

&lt;p&gt;Fixtures are a set of statically defined objects which might be injected into your database during your test run, or might just be passed to various functions in your tests. They'll either be raw &lt;code&gt;.json&lt;/code&gt; files, or they could be defined in code, looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REPOS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use-firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;starCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;erikpukinskis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;codedocs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;starCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;erikpukinskis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They will often be associated with one another, so that when they're injected into your database there will be some referential integrity. So in the example above we might also have fixtures like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;erikpukinskis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is these fixtures will tend to be used to set up many different tests. And so the fixture suite gets bigger and bigger, with lots of relationships between fixtures. It either gets really complicated to get the database into a "good" state, or you end up with a function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setUpAllTheThings&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/test/helpers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my new test suite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setUpAllTheThings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's easy enough, but it misses out on one of the &lt;strong&gt;Properties of a Good Test&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Good Test describes its preconditions&lt;/li&gt;
&lt;li&gt;A Good Test describes its expectations&lt;/li&gt;
&lt;li&gt;A Good Test only fails when things are broken&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem with &lt;code&gt;setUpAllTheThings()&lt;/code&gt; is it doesn't tell you what the conditions for success were. It just gestures at a big pile of fixtures that you have to dig through and guess what was important.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;Instead of static fixtures, your data-dependent tests should use functions called &lt;em&gt;factories&lt;/em&gt;. Over the years I have come to identify some important features for &lt;em&gt;great&lt;/em&gt; test factories.&lt;/p&gt;

&lt;p&gt;This post will teach you those patterns in three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; What makes a factory
&lt;/li&gt;
&lt;li&gt; What my factory functions look like
&lt;/li&gt;
&lt;li&gt;Additional factory patterns I love&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What makes a factory?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Factories are imperative
&lt;/h3&gt;

&lt;p&gt;Unlike "fixtures" which are set up once, globally for all tests, factories are called imperatively during test setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/test/factory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render a repo heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUpRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findByRole&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Repo&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that your test can specify the smallest number of records that need to be set up for the test to work. When the test fails, you can immediately see what the specific conditions being tested were, eliminating a bunch of guesswork.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. You can override everything in a factory
&lt;/h3&gt;

&lt;p&gt;When you use record from a test fixture, it has every single field set. It also probably has every possible association specified. And so it's never clear &lt;em&gt;which&lt;/em&gt; fields or associations are required for the test to make sense.&lt;/p&gt;

&lt;p&gt;With factories, you can specify exactly which fields are preconditions for the test's success:&lt;/p&gt;

&lt;p&gt;This lets us make a small improvement on the test above, making it more explicit that we're testing a repo with a specific name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/test/factory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render a repo heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUpRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Test Repo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findByRole&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Repo&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Test Repo)
})
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Factories auto-generate associations
&lt;/h3&gt;

&lt;p&gt;One of the key jobs of a factory is to &lt;em&gt;ensure data consistency&lt;/em&gt; by creating any and all associations needed for the database schema to be happy.&lt;/p&gt;

&lt;p&gt;This allows you to specify only the objects needed for your test preconditions without having to bootstrap a bunch of context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/server/data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;repos should have owners&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUpRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInstanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you all the ease-of-use of a giant &lt;code&gt;setUpAllTheThings()&lt;/code&gt; function, without over-specifying your preconditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Factories let you override associations
&lt;/h3&gt;

&lt;p&gt;But even better, factories can pick and choose when to do automatic setup and when to set up relations manually.&lt;/p&gt;

&lt;p&gt;For example, you might want to test some aspects of how repos and and users work together. The &lt;code&gt;setUpRepo&lt;/code&gt; factory can automatically create a user for you, but if your test depends on a user with some specific traits you can also do it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;only owners can see repo details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUpUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;otherUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUpUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUpRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Test Repo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findByRole&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queryByRole&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rerender&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SignInProvider&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Repo&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SignInProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Test Repo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;rerender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SignInProvider&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;otherUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Repo&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SignInProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;404 Not Found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;queryByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Test Repo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this is in service of the same goal: Tests with &lt;em&gt;explicit preconditions&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What my factory functions look like
&lt;/h2&gt;

&lt;p&gt;This is the typical pattern I use for setting up a factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;starCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="na"&gt;tagIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="na"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;repoCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RepoOverrides&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUpRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FirebaseApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RepoOverrides&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// It's useful to number the calls to the factory, to have a unique identifier&lt;/span&gt;
  &lt;span class="c1"&gt;// that also indicates the order of creation:&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uniqueId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;repoCount&lt;/span&gt;

  &lt;span class="c1"&gt;// Split out the associations from the object properties:&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;propertyOverrides&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;overrides&lt;/span&gt;

  &lt;span class="c1"&gt;// Create any associations that are missing:&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setUpUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Now create the primary object&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;addDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;repos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Provide fallback values:&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repo-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uniqueId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;starCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tagIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;

    &lt;span class="c1"&gt;// Then the overrides from the user:&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;propertyOverrides&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// And lastly any associations:&lt;/span&gt;
    &lt;span class="na"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;propertyOverrides&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Repo&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally, return the primary object along with all of its assocations&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it. You can build out a large factory system using just those few patterns.&lt;/p&gt;

&lt;p&gt;I don't tend to use factory libraries like &lt;a href="https://github.com/thoughtbot/fishery" rel="noopener noreferrer"&gt;Fishery&lt;/a&gt;. That's a great library, but I don't find it saves much code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional factory patterns I love
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Factories should return a set of associated objects
&lt;/h3&gt;

&lt;p&gt;Sometimes folks will set up factories to return just a single object, or maybe an object with a bunch of associations attached to it.&lt;/p&gt;

&lt;p&gt;Because "factories auto-generate associations", I recommend returning all of the associations along with the primary object. This allows you use those associations elsewhere in your test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;can navigate to repos path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUpRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Test Repo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signInAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;goToPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/repos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Test Repo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A solid set of test factories can really accelerate your team in a few ways:&lt;/p&gt;

&lt;p&gt;First, they speed up debugging time when tests fail. They make it super clear what scenario is under test, which helps developers spend less time trying to understand a test failure.&lt;/p&gt;

&lt;p&gt;Second, they make it much easier to bang out new tests. This gives you better coverage which usually means more stability in your code. It also makes developers more likely to delete obsolete tests, since they know it'll be easy to add back the correct ones.&lt;/p&gt;

&lt;p&gt;And lastly, factories reduce the dependencies between tests, which means less time playing whack-a-mole when you change a fixture to get one test to pass, and it breaks another test.&lt;/p&gt;

</description>
      <category>tdd</category>
    </item>
    <item>
      <title>How to spy on a third party ES6 export in Vitest</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Mon, 26 Jun 2023 05:31:43 +0000</pubDate>
      <link>https://dev.to/erikpuk/how-to-mock-a-third-party-es6-export-in-vitest-38ff</link>
      <guid>https://dev.to/erikpuk/how-to-mock-a-third-party-es6-export-in-vitest-38ff</guid>
      <description>&lt;p&gt;This was a bit of a tricky one, and the solution &lt;a href="https://vitest.dev/guide/mocking.html#cheat-sheet" rel="noopener noreferrer"&gt;from the Vitest docs&lt;/a&gt; didn't work for me.&lt;/p&gt;

&lt;p&gt;Those docs suggested that I could do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Firestore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Firestore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onSnapshot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSnapshot&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but that didn't work, Vitest complains that it can't redefine that propery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot redefine property: onSnapshot
 ❯ lib/useDoc.test.ts:12:4
     10| import type { Repo } from "~/test/helpers/factory"
     11| 
     12| vi.spyOn(Firestore, "onSnapshot")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  The solution
&lt;/h1&gt;

&lt;p&gt;The answer wasn't too bad, but there are actually three steps:&lt;/p&gt;

&lt;p&gt;1) Mock the import&lt;br&gt;
2) Import the module in your test&lt;br&gt;
3) Create the spy&lt;/p&gt;

&lt;p&gt;Let's walk through each of those.&lt;/p&gt;
&lt;h3&gt;
  
  
  1) Mock the import
&lt;/h3&gt;

&lt;p&gt;At the top level of my test file you need to mock the import using &lt;code&gt;vi.mock&lt;/code&gt;. In the factory you feed to &lt;code&gt;vi.mock&lt;/code&gt; you will replace the function you want to spy on with with a &lt;code&gt;vi.fn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FakeFirestore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;onSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;original&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FakeFirestore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getModule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onSnapshot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mockImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSnapshot&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that very useful &lt;code&gt;importOriginal&lt;/code&gt; function which lets me keep the Firestore module fully functional. This is because &lt;code&gt;vi.mock&lt;/code&gt; is &lt;em&gt;really&lt;/em&gt; persnickety about referencing outside variables.&lt;/p&gt;

&lt;p&gt;That factory function needs to be 100% self contained. Therefore you have to...&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Import the module in your test
&lt;/h3&gt;

&lt;p&gt;In order to have something to spy on, you'll need to import that same module directly in your test. This should be the mock object that you returned in your factory above, with the &lt;code&gt;vi.fn&lt;/code&gt; where you stuck it. That'll give us something to spy on...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Firestore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3) Create the spy
&lt;/h3&gt;

&lt;p&gt;Finally, we can create the spy, and place our expectations on it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;calls onSnapshot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Firestore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onSnapshot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="cm"&gt;/* trigger the onSnapshot call here in my code */&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSnapshot&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! My test passes.&lt;/p&gt;

&lt;p&gt;Check out the complete source code &lt;a href="https://gist.github.com/erikpukinskis/5eae9d675c004dd1ff660a46dcf5ef47" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/erikpuk" rel="noopener noreferrer"&gt;Follow me on Twitter&lt;/a&gt; if you want to see more Vite, Vitest, and Firestore tips!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltgn0z91a2zzasa2leej.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltgn0z91a2zzasa2leej.gif" alt="Image description" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vite</category>
      <category>vitest</category>
      <category>firebase</category>
      <category>testing</category>
    </item>
    <item>
      <title>The best way to initialize a React Context in TypeScript</title>
      <dc:creator>Erik Pukinskis</dc:creator>
      <pubDate>Wed, 14 Jun 2023 01:13:46 +0000</pubDate>
      <link>https://dev.to/erikpuk/the-best-way-to-initialize-a-react-context-in-typescript-59m3</link>
      <guid>https://dev.to/erikpuk/the-best-way-to-initialize-a-react-context-in-typescript-59m3</guid>
      <description>&lt;p&gt;&lt;a href="https://react.dev/reference/react/useContext" rel="noopener noreferrer"&gt;React Contexts&lt;/a&gt; are great—they let you neatly encapsulate behavior that cuts across multiple React components.&lt;/p&gt;

&lt;p&gt;But there are a few gotchas when using them in TypeScript.&lt;/p&gt;

&lt;p&gt;This article will explain how to deal with one.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can't easily initialize a React Context if it has required properties. Because you won't know what those properties are when you call &lt;code&gt;createContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My &lt;a href="https://gist.github.com/erikpukinskis/ffc080bbd087df7ee4567421c186ae13" rel="noopener noreferrer"&gt;makeUninitializedContext&lt;/a&gt; gist solves that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;A common pattern when using React Contexts is to pass some props into your Context Provider. For example, if your app has dark mode and a light mode, your design system provider might have a &lt;code&gt;theme&lt;/code&gt; prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DesignSystemProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/design-system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DesignSystemContextValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DesignSystemContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="cm"&gt;/* what goes here??? */&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MediaQueryListEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DesignSystemProvider&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trouble is, how do you then initialize your context? It's easy if you can use a default value...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DesignSystemContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... but ...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if you don't &lt;em&gt;want&lt;/em&gt; a default value?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if you want to test that your hooks behave a certain way when used outside the Context Provider?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if your Context Provider depends on data objects that have no sane default value, like a user or a workspace?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you try to create a context with an uninitialized object, TypeScript will yell at you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnq6cnoc4vchqecxspow9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnq6cnoc4vchqecxspow9.png" alt="Source code showing type error: Argument of type '{}' is not assignable to parameter of type 'DesignSystemContextValue'.&amp;lt;br&amp;gt;
Property " width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The way I solve this is by including a small snippet of code in almost every React project I work on, called  &lt;a href="https://gist.github.com/erikpukinskis/ffc080bbd087df7ee4567421c186ae13" rel="noopener noreferrer"&gt;makeUninitializedContext&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;makeUninitializedContext&lt;/code&gt; function returns a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="noopener noreferrer"&gt;Proxy object&lt;/a&gt; that is typed with whatever type you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;makeUninitializedContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/helpers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DesignSystemContextValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DesignSystemContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;makeUninitializedContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DesignSystemContextValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cannot use DesignSystemContext outside a DesignSystemProvider&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you a Context object which is properly typed...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xh3p1pxg92jr3p9s5xj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xh3p1pxg92jr3p9s5xj.png" alt="Image description" width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... and if you try to use the context object without initializing it, you get a helpful error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9jysx94kjq2ry6590c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9jysx94kjq2ry6590c0.png" alt="Screenshot of uncaught error: Cannot use DesignSystemContext outside a DesignSystemProvider: tried getting context.theme" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Making the context optional
&lt;/h3&gt;

&lt;p&gt;Sometimes you have a hook that can work in either of two ways: with or without the context.&lt;/p&gt;

&lt;p&gt;For that case, I use the &lt;code&gt;isInitialized&lt;/code&gt; function exported in &lt;a href="https://gist.github.com/erikpukinskis/ffc080bbd087df7ee4567421c186ae13" rel="noopener noreferrer"&gt;that same gist&lt;/a&gt;. It allows you to detect whether a context is initialized before you try to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isInitialized&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/helpers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useThemeName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DesignSystemContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;isInitialized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way your hook can work either inside or outside of the Context Provider. And you won't trigger an error by accessing a property on the uninitialized context object.&lt;/p&gt;

&lt;p&gt;I hope that's helpful for someone! &lt;a href="https://twitter.com/erikpuk" rel="noopener noreferrer"&gt;Follow me on Twitter&lt;/a&gt; for more React tips.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
