<?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>Forem: Sem Gebresilassie</title>
    <description>The latest articles on Forem by Sem Gebresilassie (@semosem_20).</description>
    <link>https://forem.com/semosem_20</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%2F52445%2F711f5023-aeb3-43b8-8aba-80bffbd719a2.jpeg</url>
      <title>Forem: Sem Gebresilassie</title>
      <link>https://forem.com/semosem_20</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/semosem_20"/>
    <language>en</language>
    <item>
      <title>From Idea to Maintainable UI: A Practical React/TS Sprint Workflow</title>
      <dc:creator>Sem Gebresilassie</dc:creator>
      <pubDate>Sun, 15 Feb 2026 12:26:26 +0000</pubDate>
      <link>https://forem.com/semosem_20/from-idea-to-maintainable-ui-a-practical-reactts-sprint-workflow-4fak</link>
      <guid>https://forem.com/semosem_20/from-idea-to-maintainable-ui-a-practical-reactts-sprint-workflow-4fak</guid>
      <description>&lt;p&gt;Most React projects don’t fail because React is hard. They fail because boundaries get fuzzy: API shapes aren’t explicit, components become “do-everything”, and small changes start causing unpredictable breakage.&lt;/p&gt;

&lt;p&gt;When I join a project (or start one from scratch), I use a repeatable sprint workflow to turn an idea into a UI that’s actually maintainable: predictable contracts, clear structure, and small decisions that prevent a slow slide into chaos.&lt;/p&gt;

&lt;p&gt;Below is the exact playbook I use in a 1–2 week sprint.&lt;/p&gt;




&lt;h2&gt;
  
  
  What “maintainable UI” means (in practice)
&lt;/h2&gt;

&lt;p&gt;Maintainable doesn’t mean “perfect architecture”. It means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can add a feature without fear.&lt;/li&gt;
&lt;li&gt;Onboarding doesn’t require tribal knowledge.&lt;/li&gt;
&lt;li&gt;Bugs are isolated, not contagious.&lt;/li&gt;
&lt;li&gt;The UI and the API agree on what’s true.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one (UI ↔ API alignment) is the biggest lever I’ve found for React + TypeScript.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Sprint Workflow (1–2 weeks)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Clarify the outcome (½ day)
&lt;/h3&gt;

&lt;p&gt;Before touching code, I write down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primary user flow&lt;/strong&gt; (what are we trying to make easy?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success metric&lt;/strong&gt; (what changes if we succeed?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-goals&lt;/strong&gt; (what we’re explicitly not doing in this sprint)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents the sprint from turning into “a bunch of refactors”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Lock the data contracts (day 1–2)
&lt;/h3&gt;

&lt;p&gt;If the UI doesn’t have stable data contracts, TypeScript can’t save you.&lt;/p&gt;

&lt;p&gt;I like a small “API layer” that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;centralizes requests,&lt;/li&gt;
&lt;li&gt;encodes response types,&lt;/li&gt;
&lt;li&gt;handles errors consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a lightweight pattern that scales well:&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="c1"&gt;// api/client.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ApiError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&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="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&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;apiGet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;res&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&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;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Request failed (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="k"&gt;try&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;body&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ignore parsing errors&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;ApiError&lt;/span&gt;&lt;span class="p"&gt;;&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;T&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;Then each feature defines the shapes it cares about:&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="c1"&gt;// features/projects/types.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Project&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="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&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="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;draft&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;active&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;archived&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;This isn’t complicated, but it gives you a stable “contract boundary”. Later, if you want runtime validation (Zod), it drops in cleanly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 — Component contracts: make boundaries explicit (day 2–4)
&lt;/h3&gt;

&lt;p&gt;The most common maintainability issue in React isn’t state management—it's “component sprawl”.&lt;/p&gt;

&lt;p&gt;My rule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;components should receive &lt;strong&gt;data and callbacks&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;not reach into the world to fetch things unless they’re a page-level component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A clean interface usually looks 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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectCardProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onOpen&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&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;ProjectCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onOpen&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;ProjectCardProps&lt;/span&gt;&lt;span class="p"&gt;)&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&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="nf"&gt;onOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&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="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;project&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;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;Why this helps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tests become trivial,&lt;/li&gt;
&lt;li&gt;components become reusable,&lt;/li&gt;
&lt;li&gt;debugging becomes “local”.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4 — Choose a folder structure you’ll keep (day 3–5)
&lt;/h3&gt;

&lt;p&gt;A maintainable UI needs a predictable map.&lt;/p&gt;

&lt;p&gt;Two structures I’ve seen work consistently:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A — Feature-first&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  features/
    projects/
      components/
      hooks/
      types.ts
      api.ts
      index.ts
  shared/
    ui/
    lib/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B — Route-first (if the app is mostly pages)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  routes/
    dashboard/
    settings/
  components/
  lib/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If in doubt, feature-first wins as the codebase grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 — State management: be boring on purpose (day 4–7)
&lt;/h3&gt;

&lt;p&gt;Most apps don’t need a heavyweight state solution on day one.&lt;/p&gt;

&lt;p&gt;My default stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;server state: React Query / TanStack Query&lt;/li&gt;
&lt;li&gt;local UI state: &lt;code&gt;useState&lt;/code&gt; / &lt;code&gt;useReducer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;global UI state only if truly needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is fewer moving parts. Maintainability often improves when you remove abstractions, not add them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 — Testing: a thin layer, high confidence (day 6–10)
&lt;/h3&gt;

&lt;p&gt;I prefer a small, reliable testing approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit tests&lt;/strong&gt; for pure logic (formatters, mappers, reducers)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration tests&lt;/strong&gt; for a couple of key flows&lt;/li&gt;
&lt;li&gt;Avoid shallow tests that only mirror implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You want tests that answer: “If we ship this change, did we break the product?”&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 — Performance / UX: remove the obvious pain (day 7–12)
&lt;/h3&gt;

&lt;p&gt;In a sprint, performance work should be pragmatic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defer expensive animations until after first paint&lt;/li&gt;
&lt;li&gt;reduce unnecessary rerenders&lt;/li&gt;
&lt;li&gt;keep mobile layouts calm and readable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Small improvements here compound quickly because they reduce future friction.&lt;/p&gt;




&lt;h2&gt;
  
  
  The end result
&lt;/h2&gt;

&lt;p&gt;After this workflow, you typically end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clearer UI → API boundaries&lt;/li&gt;
&lt;li&gt;smaller components with explicit contracts&lt;/li&gt;
&lt;li&gt;a folder structure that doesn’t fight you&lt;/li&gt;
&lt;li&gt;“boring” state that stays understandable&lt;/li&gt;
&lt;li&gt;a couple of tests that matter&lt;/li&gt;
&lt;li&gt;better mobile readability/performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s what maintainable feels like: fast changes, fewer surprises.&lt;/p&gt;




&lt;h2&gt;
  
  
  If you want help applying this to your codebase
&lt;/h2&gt;

&lt;p&gt;If you’re building a React/TypeScript product (or inheriting a messy one) and want to tighten it up in a focused sprint, you can find me here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://akukulu.com" rel="noopener noreferrer"&gt;https://akukulu.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(There’s a booking link on the page.)&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>frontend</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Twelve‑Factor App: Codebase + Dependencies (practical intro)</title>
      <dc:creator>Sem Gebresilassie</dc:creator>
      <pubDate>Tue, 15 Sep 2020 11:37:30 +0000</pubDate>
      <link>https://forem.com/semosem_20/the-twelve-factor-app-codebase-km4</link>
      <guid>https://forem.com/semosem_20/the-twelve-factor-app-codebase-km4</guid>
      <description>&lt;p&gt;If you build software that runs as a service (SaaS, APIs, web apps), the &lt;strong&gt;Twelve‑Factor App&lt;/strong&gt; is still one of the clearest checklists for keeping things deployable and maintainable.&lt;/p&gt;

&lt;p&gt;In my experience, two factors quietly cause a big share of real-world pain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;environments that drift (“staging is not prod”)&lt;/li&gt;
&lt;li&gt;setups that aren’t repeatable (“it works on my machine”)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post is a short, practical intro to &lt;strong&gt;two factors&lt;/strong&gt; that prevent those problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Codebase&lt;/strong&gt; — one repo, many deploys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt; — declare everything explicitly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want the full set of factors, the official guide is here (worth reading end-to-end):&lt;br&gt;
&lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;https://12factor.net/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1) Codebase — one repo, many deploys
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In plain language:&lt;/strong&gt; your app should have a single source of truth in version control (one codebase), and that same codebase should be deployed to multiple environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this looks like in practice
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;One repository deployed to &lt;strong&gt;dev&lt;/strong&gt;, &lt;strong&gt;staging&lt;/strong&gt;, and &lt;strong&gt;production&lt;/strong&gt; through the same pipeline.&lt;/li&gt;
&lt;li&gt;You promote the &lt;em&gt;same code&lt;/em&gt; forward (instead of rebuilding a different app per environment).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common failure modes I see
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A production hotfix is made directly in prod and never merged back.&lt;/li&gt;
&lt;li&gt;“Staging” is pointed at a different repo/branch for convenience and silently diverges.&lt;/li&gt;
&lt;li&gt;Teams copy/paste a shared “utils” folder into multiple repos and call it “shared code”.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Treat envs as &lt;strong&gt;deploys&lt;/strong&gt; of the same codebase.&lt;/li&gt;
&lt;li&gt;If multiple repos truly need shared functionality, extract a &lt;strong&gt;versioned dependency&lt;/strong&gt; (package/library) instead of duplicating code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2) Dependencies — declare everything explicitly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In plain language:&lt;/strong&gt; the app should not rely on tools/libraries “just being installed” on a server or a developer’s laptop.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this looks like in practice
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Dependencies are declared in a manifest (&lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;Gemfile&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;CI uses a deterministic install (&lt;code&gt;npm ci&lt;/code&gt;, locked versions, reproducible builds).&lt;/li&gt;
&lt;li&gt;A new developer can clone the repo and get running without guessing what to install globally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common failure modes I see
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Local uses one Node version; CI uses another; production uses a third.&lt;/li&gt;
&lt;li&gt;No lockfile / loose ranges — builds change over time without code changes.&lt;/li&gt;
&lt;li&gt;Global installs hide missing dependencies until deployment day.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pin versions where it matters, commit the lockfile, and make CI match local setup.&lt;/li&gt;
&lt;li&gt;Prefer repeatable install commands (e.g. &lt;code&gt;npm ci&lt;/code&gt; in CI).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Read the rest (highly recommended)
&lt;/h2&gt;

&lt;p&gt;The original Twelve‑Factor guide is short and extremely readable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Codebase: &lt;a href="https://12factor.net/codebase" rel="noopener noreferrer"&gt;https://12factor.net/codebase&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dependencies: &lt;a href="https://12factor.net/dependencies" rel="noopener noreferrer"&gt;https://12factor.net/dependencies&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full list: &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;https://12factor.net/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  If you want help applying this to a real codebase
&lt;/h2&gt;

&lt;p&gt;If you’re building a React/TypeScript product (or inheriting a messy one) and want to tighten up your deployment + maintainability fundamentals in a focused sprint, you can find me here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://akukulu.com" rel="noopener noreferrer"&gt;https://akukulu.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>saas</category>
      <category>devops</category>
      <category>bestpractices</category>
    </item>
  </channel>
</rss>
