<?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: Hayati</title>
    <description>The latest articles on DEV Community by Hayati (@hayatibis).</description>
    <link>https://dev.to/hayatibis</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F542187%2F00f285db-17b3-4193-802f-d22677c16190.png</url>
      <title>DEV Community: Hayati</title>
      <link>https://dev.to/hayatibis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hayatibis"/>
    <language>en</language>
    <item>
      <title>It's not a sprint, it's a marathon</title>
      <dc:creator>Hayati</dc:creator>
      <pubDate>Sun, 01 Nov 2026 06:33:40 +0000</pubDate>
      <link>https://dev.to/hayatibis/its-not-a-sprint-its-a-marathon-4p4</link>
      <guid>https://dev.to/hayatibis/its-not-a-sprint-its-a-marathon-4p4</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://hayatibis.dev/writing/its-not-a-sprint-its-a-marathon" rel="noopener noreferrer"&gt;hayatibis.dev&lt;/a&gt;, where the figures move and the toys are interactive.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Years ago I had English practice sessions with a British friend, Andrew. After the second one I told him I didn’t think I’d reached the level I wanted. He said: “It’s not a sprint, it’s a marathon.”&lt;/p&gt;

&lt;p&gt;It stuck with me, because it’s true for most things in life. Learning a language, sport, a career. It’s also true for software, which is funny, because software named its steady unit of work after the dash.&lt;/p&gt;

&lt;h2&gt;
  
  
  The irony in the word “sprint”
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://scrumguides.org/scrum-guide.html" rel="noopener noreferrer"&gt;Scrum Guide&lt;/a&gt;, the short official definition of Scrum by its two creators, says: “Sprints are the heartbeat of Scrum, where ideas are turned into value.” They are “fixed length events of one month or less to create consistency.”&lt;/p&gt;

&lt;p&gt;A heartbeat is the opposite of a sprint. It’s the pace you keep for your whole life. The name is a sprint, but the definition really describes a rhythm.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://agilemanifesto.org/principles.html" rel="noopener noreferrer"&gt;Agile Manifesto&lt;/a&gt; had already said it plainly in 2001: “Agile processes promote sustainable development. The sponsors, developers, and users should be able to maintain a constant pace indefinitely.”&lt;/p&gt;

&lt;p&gt;These days we’ve left Scrum and the agile ceremonies behind and work from a kanban to-do board. The name on the board changes nothing, though. We can’t work day and night under any method. What I want is systems that protect the balance between work and life, and that can also protect themselves without someone standing over them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a marathon pace looks like
&lt;/h2&gt;

&lt;p&gt;The example I can show is this site. One essay a week, Sunday morning, not five in a burst. The Sunday run starts at 09:07 on its own, rebuilds the site and sends the copies out. If a draft still has a gap in it, the production build refuses to ship it. Nobody has to be awake for that.&lt;/p&gt;

&lt;p&gt;That’s what I mean by a system that protects itself. It isn’t my willpower that keeps the pace, really; it’s the parts of the system.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8yts5vlns4ubvxtw29qr.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8yts5vlns4ubvxtw29qr.png" alt="Illustrative numbers. A small mill engine with a Watt flyball governor. Turn up the steam, the way agents push harder, and with the governor on the balls swing out, lift the sleeve, and the bell crank and the long drag link close the throttle: the flywheel only goes from 60 to 71 or 74 rpm. Take the governor off and the same push runs it up to 300 rpm; at 1,200 the flywheel and the arms can’t hold, and the engine flies apart. Nobody stands at the valve; the parts keep the pace. The drawing turns at half speed, and past a point it can’t show how fast." width="800" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Illustrative numbers. A small mill engine with a Watt flyball governor. Turn up the steam, the way agents push harder, and with the governor on the balls swing out, lift the sleeve, and the bell crank and the long drag link close the throttle: the flywheel only goes from 60 to 71 or 74 rpm. Take the governor off and the same push runs it up to 300 rpm; at 1,200 the flywheel and the arms can’t hold, and the engine flies apart. Nobody stands at the valve; the parts keep the pace. The drawing turns at half speed, and past a point it can’t show how fast.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/its-not-a-sprint-its-a-marathon" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents never get tired
&lt;/h2&gt;

&lt;p&gt;Andrej Karpathy wrote a long &lt;a href="https://x.com/karpathy/status/2015883857489522876" rel="noopener noreferrer"&gt;set of notes&lt;/a&gt; in January after weeks of coding with Claude. One part is about tenacity: “They never get tired, they never get demoralized, they just keep going and trying things where a person would have given up long ago to fight another day.” And then: “You realize that stamina is a core bottleneck to work and that with LLMs in hand it has been dramatically increased.”&lt;/p&gt;

&lt;p&gt;So the agent can sprint all day. We can’t, and the work we keep is exactly the part that needs a rested head: reading, reviewing, deciding, owning the thing next year.&lt;/p&gt;

&lt;p&gt;We have to move at the speed agents produce, but at a speed we can accept, and without breaking the systems they touch. That means stopping the agent’s mistakes before they reach anything, instead of catching them by hand at midnight.&lt;/p&gt;

&lt;p&gt;Karpathy names those mistakes too. “The most common category is that the models make wrong assumptions on your behalf and just run along with them without checking.” They overcomplicate, bloat abstractions and don’t clean up dead code. Forrest Chang turned these notes into a &lt;a href="https://github.com/forrestchang/andrej-karpathy-skills" rel="noopener noreferrer"&gt;single CLAUDE.md&lt;/a&gt; that you can install as a Claude skill[1]. It’s a small file, and it does the standing-over for you.&lt;/p&gt;

&lt;p&gt;But the point isn’t only the code. A healthy setup is an orchestration: instructions the agent reads, tests that pin behaviour, a gate before production, a schedule, and a person who reviews at a pace they can keep. Each part is healthy on its own and they work together. Take one out and the others have to sprint to cover it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The work didn’t shrink
&lt;/h2&gt;

&lt;p&gt;Since agents came into our lives, our workload has gone up enormously. We still have to stay regulated and keep to a sustainable level. I’m not the only one who sees it.&lt;/p&gt;

&lt;p&gt;Aruna Ranganathan and Xingqi Maggie Ye studied 200 employees at a US technology company from April to December 2025 and wrote it up in Harvard Business Review as “&lt;a href="https://hbr.org/2026/02/ai-doesnt-reduce-work-it-intensifies-it" rel="noopener noreferrer"&gt;AI Doesn’t Reduce Work—It Intensifies It&lt;/a&gt;”. They saw people “manually writing code while AI generated an alternative version, running multiple agents in parallel”. The result was “a continual switching of attention, frequent checking of AI outputs, and a growing number of open tasks.”&lt;/p&gt;

&lt;p&gt;Karpathy says the same about himself: “the main effect is that I do a lot more than I was going to do”. &lt;a href="https://simonwillison.net/2026/Feb/9/ai-intensifies-work/" rel="noopener noreferrer"&gt;Simon Willison&lt;/a&gt;, a &lt;a href="https://arxiv.org/abs/2510.07435" rel="noopener noreferrer"&gt;survey of 442 developers&lt;/a&gt; and Google’s &lt;a href="https://dora.dev/research/2025/dora-report/" rel="noopener noreferrer"&gt;DORA report&lt;/a&gt; land in the same place: the output goes up, so does the load, and AI amplifies the strengths and weaknesses a team already has. A team that already sprints will sprint harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every developer is a condom now
&lt;/h2&gt;

&lt;p&gt;In July a joke went around X. Ariel Kwiat wrote:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpu68txnxquti4rjvprsi.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpu68txnxquti4rjvprsi.png" alt="𝕏Ariel @ArielKwiat" width="798" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;𝕏Ariel @ArielKwiat&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/its-not-a-sprint-its-a-marathon" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Within a day other engineers were saying it about their own agents.&lt;/p&gt;

&lt;p&gt;It’s funny because it’s half true. We are the thin layer between what the agent writes and production. But a condom you have to put on by hand, tired, at one in the morning, is not a great protection plan :) I’d rather put that layer in the system: the skill file, the tests, the gate. Then the human can review on Monday morning, rested. And with workflow engineering, a world is coming where we aren’t the reviewer at every stage. By then the protection has to be in the system already.&lt;/p&gt;

&lt;p&gt;A paper is going around X these days as the death certificate of programming. Its first title really was “The End of Software Engineering”. Six days later its author, Zhenfeng Cao, renamed it “&lt;a href="https://arxiv.org/abs/2606.05608" rel="noopener noreferrer"&gt;Agentic Software&lt;/a&gt;”, and the conclusion now says: “The old software engineering is not ending; it is growing into something larger.” The core idea is still close to mine, though: human capacity for complexity is “essentially constant”, code becomes “a transient artifact”, and people become “intent architects, agent coordinators, and outcome auditors”. Writing code has stopped being human work; what’s left is intent, and running a system that stays healthy. The EvoClaw result the paper cites backs that up: agents’ scores drop “from &amp;gt;80% on isolated tasks to at most 38% in continuous settings”.&lt;/p&gt;

&lt;p&gt;AI really has changed our work, and there’s no point pretending it hasn’t. Building and running that system will be the job of the professionals in that field, until AI takes this work over too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to the second lesson
&lt;/h2&gt;

&lt;p&gt;Andrew was right after the second lesson, and he’d be right after the second week with a new agent too. What pace could you keep for a year, and which part of your system keeps it when you’re not looking?&lt;/p&gt;




&lt;h2&gt;
  
  
  Stands on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://agilemanifesto.org/principles.html" rel="noopener noreferrer"&gt;Principles behind the Agile Manifesto (2001)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://scrumguides.org/scrum-guide.html" rel="noopener noreferrer"&gt;Ken Schwaber and Jeff Sutherland: The Scrum Guide (2020)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://x.com/karpathy/status/2015883857489522876" rel="noopener noreferrer"&gt;Andrej Karpathy: notes from claude coding (X, 26 January 2026)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/forrestchang/andrej-karpathy-skills" rel="noopener noreferrer"&gt;Forrest Chang: andrej-karpathy-skills, a CLAUDE.md from Karpathy's notes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://hbr.org/2026/02/ai-doesnt-reduce-work-it-intensifies-it" rel="noopener noreferrer"&gt;Aruna Ranganathan and Xingqi Maggie Ye: AI Doesn't Reduce Work—It Intensifies It (Harvard Business Review, 2026)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://simonwillison.net/2026/Feb/9/ai-intensifies-work/" rel="noopener noreferrer"&gt;Simon Willison: AI intensifies work (2026)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://arxiv.org/abs/2510.07435" rel="noopener noreferrer"&gt;Zixuan Feng, Sadia Afroz, Anita Sarma, From Gains to Strains: Modeling Developer Burnout with GenAI Adoption (2025)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://dora.dev/research/2025/dora-report/" rel="noopener noreferrer"&gt;DORA: State of AI-assisted Software Development 2025&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://x.com/ArielKwiat/status/2080283937406890040" rel="noopener noreferrer"&gt;Ariel Kwiat on X (23 July 2026)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It has four rules: think before coding, simplicity first, surgical changes, and goal-driven execution (“Define success criteria. Loop until verified.”).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;A new essay every Sunday: &lt;a href="https://buttondown.com/hayatibis" rel="noopener noreferrer"&gt;get it by email&lt;/a&gt;. If you'd like more of these, you can support the writing on &lt;a href="https://www.patreon.com/cw/hayatibis" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;, or &lt;a href="https://buymeacoffee.com/hayatibis" rel="noopener noreferrer"&gt;buy me a coffee&lt;/a&gt; once.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>ai</category>
    </item>
    <item>
      <title>Make it work, make it right, make it fast</title>
      <dc:creator>Hayati</dc:creator>
      <pubDate>Sun, 25 Oct 2026 07:08:54 +0000</pubDate>
      <link>https://dev.to/hayatibis/make-it-work-make-it-right-make-it-fast-1im3</link>
      <guid>https://dev.to/hayatibis/make-it-work-make-it-right-make-it-fast-1im3</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://hayatibis.dev/writing/make-it-work-make-it-right-make-it-fast" rel="noopener noreferrer"&gt;hayatibis.dev&lt;/a&gt;, where the figures move and the toys are interactive.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This line has stuck in a corner of my mind for years. I used to read a lot of Medium posts and books like that, and I’ve shared this order with plenty of people since. When I asked myself where I’d picked it up and when it became a habit, I dug a little deeper and found it credited to Kent Beck. It’s actually older. Stephen Johnson and Brian Kernighan wrote it in &lt;em&gt;Byte&lt;/em&gt; in 1983, about C: “the strategy is definitely: first make it work, then make it right, and, finally, make it fast.”&lt;/p&gt;

&lt;p&gt;People quote it as three things to do. I read it as an order, where each step has its own definition of done and makes the next one safe.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4sj9hihd4iio2srwjksq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4sj9hihd4iio2srwjksq.png" alt="Fig. 1 · Three gates, each with its own 'done'. Fast is a loop of measuring. The shortcut from working code straight to fast code is the one to avoid." width="799" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 1 · Three gates, each with its own 'done'. Fast is a loop of measuring. The shortcut from working code straight to fast code is the one to avoid.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/make-it-work-make-it-right-make-it-fast" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it work
&lt;/h2&gt;

&lt;p&gt;Done means the tests for the behaviour you need are passing. It doesn’t have to be pretty, and it certainly doesn’t have to be fast. It has to be correct, and provably so, because the next step depends on those tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it right
&lt;/h2&gt;

&lt;p&gt;Now you clean up, and the tests stay green while you do. That is refactoring in the strict sense from &lt;a href="https://hayatibis.dev/writing/a-refactor-without-tests-is-a-rewrite" rel="noopener noreferrer"&gt;principle 01&lt;/a&gt;: new structure, same behaviour.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast" rel="noopener noreferrer"&gt;C2 wiki&lt;/a&gt; has a long argument about what “right” means. Some say clean design, others say the edge cases handled properly. I think it’s both: the special cases covered, and code you’d be happy to change next month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it fast
&lt;/h2&gt;

&lt;p&gt;Two people who earned the right to say it have already warned us about this step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doi.org/10.1145/356635.356640" rel="noopener noreferrer"&gt;Donald Knuth, in 1974&lt;/a&gt;: “We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.” The second sentence usually gets left out. &lt;strong&gt;Knuth wasn’t against optimizing. He was against guessing where to optimize.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.lysator.liu.se/c/pikestyle.html" rel="noopener noreferrer"&gt;Rob Pike, in 1989&lt;/a&gt;: “You can’t tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you’ve proven that’s where the bottleneck is.” His next rule is a single word: “Measure.”&lt;/p&gt;

&lt;p&gt;Try it yourself:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshk9mz0xpqgmoijrrzev.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshk9mz0xpqgmoijrrzev.png" alt="Fig. 2 · An illustrative profile, not a real measurement. The shape is the point: one part overwhelms the rest, and it's rarely the one people bet on." width="799" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 2 · An illustrative profile, not a real measurement. The shape is the point: one part overwhelms the rest, and it's rarely the one people bet on.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/make-it-work-make-it-right-make-it-fast" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Once you have a measurement, “fast” becomes a loop: change the hot part, measure again, stop when you hit the target. That last bit matters. Without a target, you never know when you’re done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the order matters
&lt;/h2&gt;

&lt;p&gt;Making something fast before it's right is often one of our most obvious mistakes. Clever tricks on top of messy code may not give you anything useful, and in the next change we end up wrestling with them. Trying to make something right before it works has its cost too: you're polishing something that can't do its job yet, which is about as pointless as polishing a torn shoe.&lt;/p&gt;

&lt;p&gt;I went looking for who first said these three steps and ended up with two people writing about C in 1983. More than forty years on, the languages have changed and agents write a lot of the code, but the order hasn’t. When did you last make something fast, and had you measured first?&lt;/p&gt;




&lt;h2&gt;
  
  
  Stands on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast" rel="noopener noreferrer"&gt;C2 wiki: Make It Work Make It Right Make It Fast&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Stephen C. Johnson and Brian W. Kernighan, The C Language and Models for Systems Programming (Byte, August 1983) (1983)&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://doi.org/10.1145/356635.356640" rel="noopener noreferrer"&gt;Donald E. Knuth, Structured Programming with go to Statements (ACM Computing Surveys 6(4)) (1974)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.lysator.liu.se/c/pikestyle.html" rel="noopener noreferrer"&gt;Rob Pike: Notes on Programming in C (1989)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;A new essay every Sunday: &lt;a href="https://buttondown.com/hayatibis" rel="noopener noreferrer"&gt;get it by email&lt;/a&gt;. If you'd like more of these, you can support the writing on &lt;a href="https://www.patreon.com/cw/hayatibis" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;, or &lt;a href="https://buymeacoffee.com/hayatibis" rel="noopener noreferrer"&gt;buy me a coffee&lt;/a&gt; once.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>performance</category>
    </item>
    <item>
      <title>Shipping software with a pilot's checklist</title>
      <dc:creator>Hayati</dc:creator>
      <pubDate>Sun, 18 Oct 2026 10:05:53 +0000</pubDate>
      <link>https://dev.to/hayatibis/shipping-software-with-a-pilots-checklist-4kf4</link>
      <guid>https://dev.to/hayatibis/shipping-software-with-a-pilots-checklist-4kf4</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://hayatibis.dev/writing/software-moves-with-checklists" rel="noopener noreferrer"&gt;hayatibis.dev&lt;/a&gt;, where the figures move and the toys are interactive.&lt;/em&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faywp012zziz855hxhpba.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faywp012zziz855hxhpba.png" alt="The Model 299, drawn loosely: engines running, gust lock still on. The red streamer is my addition, so you can find the lock." width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Model 299, drawn loosely: engines running, gust lock still on. The red streamer is my addition, so you can find the lock.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/software-moves-with-checklists" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I love watching &lt;em&gt;Air Crash Investigation&lt;/em&gt;, and I’d rather hear it in the Turkish dub than in the original, because of the voice of the Turkish dubbing artist &lt;a href="https://www.seslendirme.org/fatih-ozacun/" rel="noopener noreferrer"&gt;Fatih Özacun&lt;/a&gt;. I don’t know where my love of aviation comes from, but I’ve always loved flying, aircraft, air travel, and idly watching a plane heading off into the distance.&lt;/p&gt;

&lt;p&gt;This story could have been one of those episodes. On 30 October 1935, at Wright Field in Ohio, Boeing flew the most advanced bomber anyone had built. The Model 299 climbed, stalled and fell. Two of the five people on board died, the pilot among them.&lt;/p&gt;

&lt;p&gt;The pilot was Major Ployer Hill, the Army Air Corps’ chief of flight testing. Few pilots anywhere had more experience. He had forgotten one small thing: the gust lock[1] was still engaged. A newspaper called the 299 “too much airplane for one man to fly.”&lt;/p&gt;

&lt;p&gt;More training was hard to justify; nobody was going to out-train the Army’s chief test pilot. So a group of test pilots wrote the steps for taxi, takeoff, flight and landing on a card. In Gawande’s account, the 299 then flew 1.8 million miles without an accident, and the Army ordered almost thirteen thousand of them. You know it as the B-17.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to forget
&lt;/h2&gt;

&lt;p&gt;The 299 shows the first: too many steps for one head. A new system, or a migration on a table you’ve never touched. Nothing is routine yet, so everything has to be remembered at once.&lt;/p&gt;

&lt;p&gt;The second is the opposite. The step you have done a thousand times is the one you skip, because skipping it has never hurt. You don’t test the rollback because it worked last time. You assume the flag is off because it usually is.&lt;/p&gt;

&lt;p&gt;Both end with a step that only existed in someone’s head. Engineers need a better place to keep those steps than their own memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of checklist
&lt;/h2&gt;

&lt;p&gt;Daniel Boorman was designing flight-deck checklists at Boeing when Atul Gawande, a surgeon, went to see him while writing &lt;a href="https://atulgawande.com/book/the-checklist-manifesto/" rel="noopener noreferrer"&gt;&lt;em&gt;The Checklist Manifesto&lt;/em&gt;&lt;/a&gt;, a book about why checklists work in cockpits and how they could work in operating rooms. The first thing Boorman asks is which kind of checklist you need.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00hlrpm829vnr456piec.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00hlrpm829vnr456piec.png" alt="Fig. 1 · Read-do walks the list one line at a time. Do-confirm works from memory, stops at the pause point, then checks." width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 1 · Read-do walks the list one line at a time. Do-confirm works from memory, stops at the pause point, then checks.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/software-moves-with-checklists" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read-do&lt;/strong&gt;: read the line, then do it. This is for things you do rarely and under stress: the incident runbook, the database restore. You are not expected to remember; you are expected to follow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do-confirm&lt;/strong&gt;: do the work from memory, then stop and check it against the list. This is the pull request template and the release check. It trusts your skill and catches the slip.&lt;/p&gt;

&lt;p&gt;Software checklists often fail by being the wrong kind. A pull request template written as twelve numbered steps is a read-do list in a do-confirm moment: nobody opens a PR and follows a template line by line, so people tick the whole thing at the end without reading it. A restore runbook that says “verify the backups are healthy” is a do-confirm list in a read-do moment. At three in the morning you need the exact command, and the runbook only reminds you that one exists.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1fyijc5tfnkvirhroxlq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1fyijc5tfnkvirhroxlq.png" alt="Fig. 2 · Pick the kind for each moment." width="799" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 2 · Pick the kind for each moment.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/software-moves-with-checklists" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes one work
&lt;/h2&gt;

&lt;p&gt;Boorman’s rules, as Gawande reports them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;A pause point.&lt;/strong&gt; A checklist belongs to a moment, like before merge or right after a deploy. Not “whenever”.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Killer items.&lt;/strong&gt; His term for the steps that hurt when skipped and get skipped anyway. Everything else goes somewhere else.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Short.&lt;/strong&gt; His rule of thumb is five to nine items. Spend much more than a minute at a pause point and people start shortcutting.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Tested.&lt;/strong&gt; Try it in a rehearsal, then revise it when real use exposes a gap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“Short” sounds like it rules out a fifty-step database restore. It doesn’t. The checklist only has to be short enough to use at its pause point; the detailed steps live in the procedure it points to.&lt;/p&gt;

&lt;p&gt;Asaf Degani and Earl Wiener, two human-factors researchers, &lt;a href="https://ntrs.nasa.gov/citations/19910017830" rel="noopener noreferrer"&gt;studied airline checklists for NASA&lt;/a&gt; and added a rule about the answers: a response should give the item’s actual state or value, not the fact that you looked at it. They quote a pilot who answered “checked and set” during taxi and found out on the takeoff roll that the speed bugs were still on the approach setting. His verdict: “‘checked’ and ‘set’ can be said too easily without any sound verification.”&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbavun4ljmj1ndmkjz9k.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbavun4ljmj1ndmkjz9k.png" alt="Fig. 3 · Speeds made up for the toy. The story is the one Degani and Wiener quote." width="799" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 3 · Speeds made up for the toy. The story is the one Degani and Wiener quote.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/software-moves-with-checklists" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Software needs one more rule on top of theirs: every item needs a clear condition for done. “Dashboards: watched” means nothing unless someone knows what to watch, for how long, and when to stop the rollout.&lt;/p&gt;

&lt;p&gt;Here is a deploy card written with all of that in mind. The grey lines are checked by the pipeline; they stay on the card so the crew knows they exist. The rest belongs to a person. Tap a line to call it out.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw53lvhohf7uob8udrmcc.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw53lvhohf7uob8udrmcc.png" alt="Fig. 4 · A deploy card. Grey lines belong to the pipeline, every other line has an owner. The baseline and the thirty minutes are examples: pick your own, and write them down." width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 4 · A deploy card. Grey lines belong to the pipeline, every other line has an owner. The baseline and the thirty minutes are examples: pick your own, and write them down.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/software-moves-with-checklists" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Each response is a state someone can check. “Rollback-safe” means the previous release still runs against the new schema; if it doesn’t, the rollback line below it is a lie. “Last release” names the version you would go back to.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the checklist becomes code
&lt;/h2&gt;

&lt;p&gt;In software, some checklist items can become enforced checks in the delivery pipeline. Aviation does a version of this too. On the Boeing 777-9, the &lt;a href="https://www.boeing.com/content/dam/boeing/boeingdotcom/commercial/mmel/777_9_FAA_PMMEL_02262026_5Ea.pdf" rel="noopener noreferrer"&gt;electronic checklist&lt;/a&gt; reads some items from the aircraft itself: if the folding wingtips aren’t extended, it marks the Before Takeoff checklist incomplete and raises a caution.&lt;/p&gt;

&lt;p&gt;The clearest case I know is a gateway setup I built at work, which KrakenD later wrote up as a case study. Each backend team owns the files for its own routes, and those files compile, together with shared templates, into one configuration of more than 500 routes. Checking that the whole thing still builds after a one-line change is exactly the kind of step people skip. So on every pull request a pre-check builds the full configuration, and only a pull request that builds goes on to review.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnvd3fjo59uhf98npugkv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnvd3fjo59uhf98npugkv.png" alt="Fig. 5 · A pull request that doesn't build goes back to its author. One that builds still waits for a person from the owning team." width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 5 · A pull request that doesn't build goes back to its author. One that builds still waits for a person from the owning team.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/software-moves-with-checklists" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;What the pre-check can’t decide stays with a person: a code owner from the owning team has to approve before anything merges. The pipeline only checks the conditions someone wrote down. Whether the change is a good idea is still a human call, and that is where the attention should go.&lt;/p&gt;

&lt;p&gt;A checklist needs an owner, like any other code. After an incident or a rehearsal, fix the lines that were unclear, and grey out the ones the pipeline now enforces so nobody checks them twice. The lines that need someone’s judgment stay, even when a script could tick them.&lt;/p&gt;

&lt;p&gt;Gawande names the resistance plainly: using a checklist “feels beneath us,” an embarrassment for people who are supposed to be good.&lt;/p&gt;

&lt;p&gt;So go back to that morning at Wright Field. In the cockpit sits the Army’s most experienced test pilot; nobody knows flying better. What brings the airplane down is a small lock left on the tail. The pilots who came after him weren’t more talented. They had a card, that’s all. With it, the same design flew 1.8 million miles without an accident and became the B-17.&lt;/p&gt;

&lt;p&gt;Before you press deploy next time, ask yourself one thing: what’s my gust lock?&lt;/p&gt;




&lt;h2&gt;
  
  
  Stands on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://atulgawande.com/book/the-checklist-manifesto/" rel="noopener noreferrer"&gt;Atul Gawande, The Checklist Manifesto: How to Get Things Right (2009)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.newyorker.com/magazine/2007/12/10/the-checklist" rel="noopener noreferrer"&gt;Atul Gawande, The Checklist (The New Yorker, 10 December 2007)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://ntrs.nasa.gov/citations/19910017830" rel="noopener noreferrer"&gt;Asaf Degani and Earl L. Wiener, Human Factors of Flight-Deck Checklists: The Normal Checklist (NASA CR-177549) (1990)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.boeing.com/content/dam/boeing/boeingdotcom/commercial/mmel/777_9_FAA_PMMEL_02262026_5Ea.pdf" rel="noopener noreferrer"&gt;Boeing 777-9 Proposed Master Minimum Equipment List, item 31-61-11: Electronic Checklist (FAA, 2026)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.krakend.io/case-study/paribu/" rel="noopener noreferrer"&gt;KrakenD: Every Change Reviewed, Every Peak Absorbed (Paribu case study)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A gust lock pins the control surfaces so wind can’t slam them around while the aircraft is parked. Essential on the ramp, fatal on the runway.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;A new essay every Sunday: &lt;a href="https://buttondown.com/hayatibis" rel="noopener noreferrer"&gt;get it by email&lt;/a&gt;. If you'd like more of these, you can support the writing on &lt;a href="https://www.patreon.com/cw/hayatibis" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;, or &lt;a href="https://buymeacoffee.com/hayatibis" rel="noopener noreferrer"&gt;buy me a coffee&lt;/a&gt; once.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>aviation</category>
    </item>
    <item>
      <title>A refactor without tests is a rewrite</title>
      <dc:creator>Hayati</dc:creator>
      <pubDate>Sun, 11 Oct 2026 11:48:40 +0000</pubDate>
      <link>https://dev.to/hayatibis/a-refactor-without-tests-is-a-rewrite-4jpp</link>
      <guid>https://dev.to/hayatibis/a-refactor-without-tests-is-a-rewrite-4jpp</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://hayatibis.dev/writing/a-refactor-without-tests-is-a-rewrite" rel="noopener noreferrer"&gt;hayatibis.dev&lt;/a&gt;, where the figures move and the toys are interactive.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;“I’m refactoring it” is one of the most stretched sentences in software. It gets used for renaming a variable, and for a service that is down for a week while someone cleans it up.&lt;/p&gt;

&lt;p&gt;Martin Fowler, who wrote &lt;a href="https://martinfowler.com/books/refactoring.html" rel="noopener noreferrer"&gt;the book&lt;/a&gt; on it, &lt;a href="https://martinfowler.com/bliki/DefinitionOfRefactoring.html" rel="noopener noreferrer"&gt;puts it more precisely&lt;/a&gt;. A refactoring is “a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.”&lt;/p&gt;

&lt;p&gt;The interesting part is the end of that sentence: &lt;em&gt;without changing its observable behavior&lt;/em&gt;. It raises a question people tend to skip: how do you know?&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you know nothing changed?
&lt;/h2&gt;

&lt;p&gt;You can read the diff, or try a few things in the app (these days we often don’t even read PR diffs: an AI agent writes the code, and another one reviews it :) Neither holds up once the change is bigger than a few lines.&lt;/p&gt;

&lt;p&gt;Tests are the simple answer. They pin down what the code does today, and they tell you within seconds when a step moves it. Without them, “the behaviour didn’t change” is a wish rather than a fact. A change you can only hope is safe carries the risk of a rewrite, whatever the ticket calls it.&lt;/p&gt;

&lt;p&gt;Fowler has a &lt;a href="https://martinfowler.com/bliki/RefactoringMalapropism.html" rel="noopener noreferrer"&gt;one-line test&lt;/a&gt; for this: “If somebody talks about a system being broken for a couple of days while they are refactoring, you can be pretty sure they are not refactoring.”&lt;/p&gt;

&lt;p&gt;Fowler would call the untested kind restructuring rather than a rewrite. My rule is stricter: if I can’t show the behaviour stayed put, I plan the change with the care I’d give a rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small steps, each one checked
&lt;/h2&gt;

&lt;p&gt;Refactoring, done his way, is a chain of small moves with names: Rename Variable, Extract Function, Move Function. After each one you run the tests.&lt;/p&gt;

&lt;p&gt;These are the moves we worked hard to get used to, and racked our brains over, back before AI. Now, building with Claude and Codex, we sometimes have to steer the agents down the same road. The most capable models already know all of this, and the way they are trained, to keep running the code and fixing it until the tests pass,[1] lets them do it in the natural flow of work. Still, we need to take care to hand them our own way of working.&lt;/p&gt;

&lt;p&gt;Flip the switch below and watch the same five steps with and without tests.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffgzp1b69zd83r7ljz5ao.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffgzp1b69zd83r7ljz5ao.png" alt="Fig. 1 · Five small steps. With tests, the step that changes behaviour fails at once and gets undone. Without them, the bug shows up weeks later and nobody knows which step it came from." width="799" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 1 · Five small steps. With tests, the step that changes behaviour fails at once and gets undone. Without them, the bug shows up weeks later and nobody knows which step it came from.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/a-refactor-without-tests-is-a-rewrite" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;With tests, a broken step is always the last step, so you know exactly what to undo. Without them, all five ship together. When the bug arrives, the question is no longer “what did I just do?” but “what did we do in October?”&lt;/p&gt;

&lt;h2&gt;
  
  
  The legacy code trap
&lt;/h2&gt;

&lt;p&gt;Michael Feathers put it bluntly in &lt;em&gt;Working Effectively with Legacy Code&lt;/em&gt;: legacy code is simply code without tests. Age has nothing to do with it. Last week’s code without tests is legacy code.&lt;/p&gt;

&lt;p&gt;And it comes with a trap. To change the code safely you need tests. To get tests around it you often have to change the code first: a constructor that opens a database connection, or a function that reads the clock. Feathers’ way out is a loop where the change you actually came for is the last step.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz18x1ya1zpszph1viyxm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz18x1ya1zpszph1viyxm.png" alt="Fig. 2 · Feathers' legacy code change algorithm. The first edits only make the code testable; the change you came for comes last." width="799" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 2 · Feathers' legacy code change algorithm. The first edits only make the code testable; the change you came for comes last.&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/a-refactor-without-tests-is-a-rewrite" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Step 3 is where the careful, tiny edits go: pass the clock in as a parameter, pull the database behind an interface. Feathers calls the places where you can swap behaviour without editing the code there &lt;em&gt;seams&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Step 4 has a trick I like. You don’t write tests for what the code &lt;em&gt;should&lt;/em&gt; do; you write &lt;em&gt;characterization tests&lt;/em&gt; for what it &lt;em&gt;does&lt;/em&gt;. Write an assertion you know is wrong, run it, and let the failure tell you the real answer. Then put the real answer in the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// A characterization test: it records what Price does today, not what it should do.&lt;/span&gt;
&lt;span class="c"&gt;// If today's answer looks wrong, pin it anyway and fix it in a separate change.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPrice_CurrentBehaviour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"0.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="m"&gt;199&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"1.99"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;"-0.05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="m"&gt;100000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"1,000.00"&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="n"&gt;got&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Price(%d) = %q, want %q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;want&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the refactor has a net under it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactor or rewrite?
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd1n72uqtilq5p6vn28b3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd1n72uqtilq5p6vn28b3.png" alt="Fig. 3 · Three changes. By my rule, which one is a refactor?" width="799" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 3 · Three changes. By my rule, which one is a refactor?&lt;/em&gt; (&lt;a href="https://hayatibis.dev/writing/a-refactor-without-tests-is-a-rewrite" rel="noopener noreferrer"&gt;interactive version&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  When it really is a rewrite
&lt;/h2&gt;

&lt;p&gt;Rewrites aren’t a sin. Sometimes the old code has to go. They just need a different plan, and the first step of that plan is to define the rewrite in a much clearer frame and say its name louder.&lt;/p&gt;

&lt;p&gt;A rewrite you’ve named can be planned like one: run the old and new code side by side and compare what they return, move traffic behind a flag, keep the way back open. Fowler’s &lt;a href="https://martinfowler.com/bliki/StranglerFigApplication.html" rel="noopener noreferrer"&gt;&lt;em&gt;strangler fig&lt;/em&gt;&lt;/a&gt; is the patient version of this: grow the new code around the old, a piece at a time, until the old part can be switched off.&lt;/p&gt;

&lt;p&gt;Next time someone says “I’m refactoring it”, ask the question this whole essay hangs on: how will you know nothing changed? If the answer is a test suite, let them refactor as much as they like, in small steps. If the answer is “I’ll click around”, it’s a rewrite with a nicer name, and it deserves a rewrite’s plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stands on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://martinfowler.com/bliki/DefinitionOfRefactoring.html" rel="noopener noreferrer"&gt;Martin Fowler: Definition of Refactoring (2004)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://martinfowler.com/bliki/RefactoringMalapropism.html" rel="noopener noreferrer"&gt;Martin Fowler: Refactoring Malapropism (2004)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://martinfowler.com/books/refactoring.html" rel="noopener noreferrer"&gt;Martin Fowler, Refactoring: Improving the Design of Existing Code (2nd ed.) (2018)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Michael Feathers, Working Effectively with Legacy Code (2004)&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://martinfowler.com/bliki/StranglerFigApplication.html" rel="noopener noreferrer"&gt;Martin Fowler: Strangler Fig (2024)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://openai.com/index/o3-o4-mini-codex-system-card-addendum/" rel="noopener noreferrer"&gt;OpenAI: Addendum to o3 and o4-mini system card: Codex (2025)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://code.claude.com/docs/en/best-practices" rel="noopener noreferrer"&gt;Anthropic: Best practices for Claude Code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://openai.com/index/o3-o4-mini-codex-system-card-addendum/" rel="noopener noreferrer"&gt;OpenAI says&lt;/a&gt; codex-1, the model behind Codex, was trained with reinforcement learning on real-world coding tasks and “iteratively runs tests until passing results are achieved.” Anthropic’s &lt;a href="https://code.claude.com/docs/en/best-practices" rel="noopener noreferrer"&gt;Claude Code guide&lt;/a&gt; describes the same loop: Claude “does the work, runs the check, reads the result, and iterates until the check passes.”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;If you'd like more of these, you can support the writing on &lt;a href="https://www.patreon.com/cw/hayatibis" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
