<?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: Sem Gebresilassie</title>
    <description>The latest articles on DEV Community by Sem Gebresilassie (@semosem_20).</description>
    <link>https://dev.to/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>DEV Community: Sem Gebresilassie</title>
      <link>https://dev.to/semosem_20</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/semosem_20"/>
    <language>en</language>
    <item>
      <title>4 Moves to Train Systems Thinking When AI Writes Your Code</title>
      <dc:creator>Sem Gebresilassie</dc:creator>
      <pubDate>Fri, 24 Apr 2026 19:19:26 +0000</pubDate>
      <link>https://dev.to/semosem_20/4-moves-to-train-systems-thinking-when-ai-writes-your-code-1ja7</link>
      <guid>https://dev.to/semosem_20/4-moves-to-train-systems-thinking-when-ai-writes-your-code-1ja7</guid>
      <description>&lt;p&gt;AI can now generate components, refactor functions, write tests, scaffold APIs, and ship features faster than many thought possible. For developers, this is exciting. It can remove friction, speed up delivery, and help turn ideas into working software quickly.&lt;/p&gt;

&lt;p&gt;But there is also a growing risk.&lt;/p&gt;

&lt;p&gt;If AI writes the code while you stop thinking about the system, you may become faster at producing complexity you do not fully understand.&lt;/p&gt;

&lt;p&gt;Code is only one layer. Real engineering lives in relationships: how components interact, where state lives, how data moves, what happens when things fail, and how trade-offs are made under real constraints.&lt;/p&gt;

&lt;p&gt;That is where systems thinking matters.&lt;/p&gt;

&lt;p&gt;Here are four unsexy but powerful habits to keep sharp while AI helps with implementation.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Draw the System First
&lt;/h2&gt;

&lt;p&gt;Before prompting an AI tool, take one page and sketch the system.&lt;/p&gt;

&lt;p&gt;Draw boxes for components. Draw arrows for data flow. Mark where state is stored. Mark where failures might surface.&lt;/p&gt;

&lt;p&gt;It does not need to be beautiful. It only needs to be honest.&lt;/p&gt;

&lt;p&gt;A rough diagram often reveals hidden assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two services depending on each other&lt;/li&gt;
&lt;li&gt;State duplicated in multiple places&lt;/li&gt;
&lt;li&gt;Missing ownership boundaries&lt;/li&gt;
&lt;li&gt;Fragile flows with too many steps&lt;/li&gt;
&lt;li&gt;No clear recovery path when something breaks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you cannot explain the system simply on paper, there is a good chance the generated code will reflect that confusion.&lt;/p&gt;

&lt;p&gt;AI is excellent at filling gaps. The problem is that it fills them with guesses.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Write a Small Spec
&lt;/h2&gt;

&lt;p&gt;Many developers jump straight into prompting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build me a dashboard with authentication and analytics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sounds productive, but vague requests often create vague systems.&lt;/p&gt;

&lt;p&gt;Instead, write a short spec before implementation.&lt;/p&gt;

&lt;p&gt;Keep it simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem are we solving?&lt;/li&gt;
&lt;li&gt;Who is it for?&lt;/li&gt;
&lt;li&gt;What constraints exist?&lt;/li&gt;
&lt;li&gt;What does success look like?&lt;/li&gt;
&lt;li&gt;What failure modes matter?&lt;/li&gt;
&lt;li&gt;What should not be done?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even five minutes of structured thinking changes the quality of outputs.&lt;/p&gt;

&lt;p&gt;The spec becomes scaffolding. It helps both you and the AI reason inside boundaries.&lt;/p&gt;

&lt;p&gt;In many teams today, a lightweight spec may be one of the safest tools for collaborating with coding agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Run the Deletion Test
&lt;/h2&gt;

&lt;p&gt;Pick one component in your current product and ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If I delete this, what breaks and how badly?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This question is surprisingly powerful.&lt;/p&gt;

&lt;p&gt;It reveals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real dependencies vs imagined dependencies&lt;/li&gt;
&lt;li&gt;Legacy code nobody understands&lt;/li&gt;
&lt;li&gt;Over-engineered abstractions&lt;/li&gt;
&lt;li&gt;Features nobody uses&lt;/li&gt;
&lt;li&gt;Hidden coupling across teams or services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the honest answer is &lt;em&gt;I don’t know&lt;/em&gt;, that is useful information.&lt;/p&gt;

&lt;p&gt;That uncertainty becomes your study list.&lt;/p&gt;

&lt;p&gt;Systems thinking grows when you understand consequences, not just syntax.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Study the Generated Code
&lt;/h2&gt;

&lt;p&gt;One of the easiest traps with AI is passive acceptance.&lt;/p&gt;

&lt;p&gt;The code works. Tests pass. PR merged.&lt;/p&gt;

&lt;p&gt;But if you never inspect the output, you miss the learning.&lt;/p&gt;

&lt;p&gt;Read generated code like a reviewer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why this structure?&lt;/li&gt;
&lt;li&gt;Why this dependency?&lt;/li&gt;
&lt;li&gt;Why this state model?&lt;/li&gt;
&lt;li&gt;What happens on edge cases?&lt;/li&gt;
&lt;li&gt;What assumptions are hidden?&lt;/li&gt;
&lt;li&gt;What becomes expensive later?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes AI gives elegant shortcuts. Sometimes it gives polished technical debt.&lt;/p&gt;

&lt;p&gt;Your job is not only to receive code. Your job is to evaluate systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Changes Speed, Not Responsibility
&lt;/h2&gt;

&lt;p&gt;AI can dramatically improve implementation speed.&lt;/p&gt;

&lt;p&gt;But speed without understanding can scale confusion, fragility, and maintenance costs.&lt;/p&gt;

&lt;p&gt;The future developer may write fewer lines manually, but must think more clearly than ever.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better boundaries&lt;/li&gt;
&lt;li&gt;Better abstractions&lt;/li&gt;
&lt;li&gt;Better trade-offs&lt;/li&gt;
&lt;li&gt;Better failure awareness&lt;/li&gt;
&lt;li&gt;Better judgment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Systems thinking becomes more valuable, not less.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Use AI aggressively for leverage.&lt;/p&gt;

&lt;p&gt;But keep one part of the craft fully human:&lt;/p&gt;

&lt;p&gt;The ability to see the whole system, understand consequences, and make wise decisions inside complexity.&lt;/p&gt;

&lt;p&gt;That skill compounds for years.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <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://dev.to/semosem_20/from-idea-to-maintainable-ui-a-practical-reactts-sprint-workflow-4fak</link>
      <guid>https://dev.to/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://dev.to/semosem_20/the-twelve-factor-app-codebase-km4</link>
      <guid>https://dev.to/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>
