[HN Gopher] Use the Mikado Method to do safe changes in a comple...
       ___________________________________________________________________
        
       Use the Mikado Method to do safe changes in a complex codebase
        
       Author : foenix
       Score  : 136 points
       Date   : 2026-02-26 15:03 UTC (4 days ago)
        
 (HTM) web link (understandlegacycode.com)
 (TXT) w3m dump (understandlegacycode.com)
        
       | janpot wrote:
       | In 2026, we call this "plan mode"
        
         | eblume wrote:
         | It goes a lot further than plan mode though, in fact I would
         | say the key difference of mikado refactors from waterfall
         | refactors is that you don't do all the planning up front with
         | mikado. If anything you try to do as little planning as
         | possible.
        
       | eblume wrote:
       | I've been using a form of the Mikado Method based on a specific
       | ordering of git commits (by message prefix) along with some pre
       | commit hook scripts, governed by a document:
       | https://docs.eblu.me/how-to/agent-change-process
       | 
       | I have this configured to feed in to an agent for large changes.
       | It's been working pretty well, still not perfect though... the
       | tricky part is that it is very tempting (and maybe even sometimes
       | correct) to not fully reset between mikado "iterations", but then
       | you wind up with a messy state transfer. The advantage so far has
       | been that it's easy to make progress while ditching a session
       | context "poisoned" by some failure.
        
       | dvh wrote:
       | Inherited? I wrote the thing! Customer have no money for large
       | refactoring.
        
         | agge wrote:
         | So we make it many small commitable refactorings instead :)
        
       | koakuma-chan wrote:
       | > The project doesn't compile anymore.
       | 
       | Using a programming language that has a compiler, lucky.
        
       | theo1996 wrote:
       | 1. take a well known method for problem solving basically any
       | programmer/human knows 2. slap a cool word from the land of the
       | rising sun 3.??? 4. profit! This article is painfully pretentious
       | and stealth marketing for a book
        
         | agge wrote:
         | Stealth marketing by someone completely unrelated to the book,
         | 11 years after the book is released. Seems unlikely.
        
       | charles_f wrote:
       | Write tests. Most likely those 300k lines of code contain a TESST
       | folder with 4 unit tests written by an intern who retired to
       | become a bonsai farmer in the 1990s, and none of them pass
       | anymore. Things become much less stressful if you have something
       | basic telling you you're still good.
        
         | layer8 wrote:
         | The problem with complex legacy codebases is that you don't
         | know about the myriads of edge cases the existing code is
         | covering, and that will only be discovered in production on
         | customer premises wreaking havoc two months after you shipped
         | the seemingly regression-free refactor.
        
           | mehagar wrote:
           | I agree. This is one area I'm hoping that AI tools can help
           | with. Given a complex codebase that no one understands, the
           | ability to have an agent review the code change is at least
           | better than nothing at all.
        
           | UltraSane wrote:
           | If you save a log of input on the production system you can
           | feed it to old and new versions to find any changed in
           | behavior.
        
           | ljm wrote:
           | It helps if tests are well written such that they help you
           | with refactoring, rather than just being the implementation
           | (or a tightly coupled equivalent) but with assertions in it.
           | 
           | Rare to see though. I don't think being able to write code
           | automatically means you can write decent tests. Skill needs
           | to be developed.
        
             | danparsonson wrote:
             | And the challenge with writing tests well is that it might
             | be difficult or impossible without the refactor...
        
           | nijave wrote:
           | You can infer based on code coverage. If coverage is low,
           | tests are likely insufficient and change is risky
        
         | karmakurtisaani wrote:
         | The best time to write tests was 20 years ago. The second best
         | is now, provided you've applied to all the companies with
         | better culture.
        
         | ipsento606 wrote:
         | I've been working on react and react native applications
         | professionally for over ten years, and I have never worked on a
         | project with any kind of meaningful test coverage
        
           | stronglikedan wrote:
           | over 20 years, many stacks, and same
        
           | locknitpicker wrote:
           | > I have never worked on a project with any kind of
           | meaningful test coverage
           | 
           | That says more about you and the care you put into quality
           | assurance than anything else, really.
        
             | ipsento606 wrote:
             | Presumably you mean me, and every current and former team-
             | member I've ever had? If so, you're talking about hundreds
             | of engineers.
        
             | AnimalMuppet wrote:
             | Have you ever worked at a place where you were put on an
             | existing codebase, and that code has no tests? Have you
             | ever worked at a place where, when you try to fix that,
             | management tells you that they don't have the time to do
             | so, they have to crank out new features?
             | 
             | Is ipsento606 working at such a place? I don't know, and
             | neither do you. Why do you jump to the conclusion that it's
             | their personal failing?
        
           | hackthemack wrote:
           | I have not seen tests in any code base I worked on in the
           | past 20 years. I have noticed that there is some kind of
           | sanctimonious demeanor to quite a few people that advocate
           | for tests (on comment boards). I find the reactions to
           | discussions on tests fascinating because it seems to elicit
           | very strong opinions, sort of a "do you put your shopping
           | cart back" kind of topic, but for programmers.
        
       | hidelooktropic wrote:
       | So you do things one step at a time and timebox as you go? This
       | method probably doesn't need its own name. In fact I think that's
       | just what timeboxing is.
        
         | bee_rider wrote:
         | FWIW Mikado seems to be the name of that game where you pick up
         | one stick at a time from a pile, while trying to not disturb
         | the pile. (I forget the exact rules). So it isn't as if
         | somebody is trying to name this method after themselves or
         | something, it is just an attempt at an evocative made up term.
         | Timeboxing is also, right? I mean, timeboxing is not recognized
         | by my spell checker (I'd agree that it is more intuitive
         | though).
        
           | bregma wrote:
           | Mikado is the name of an opera (by Gilbert and Sullivan) in
           | which someone is deemed to have been executed without
           | actually having been executed. Sounds like an ideal test
           | strategy to me: yes, all the tests were executed, just not
           | actually run.
        
             | zem wrote:
             | when I saw the title I was expecting a reference to the
             | opera. was wondering if they were somehow going to work in
             | the exchange "Besides, I don't see how a man can cut off
             | his own head." "A man might try." in reference to gradually
             | removing bits of the old code.
        
           | kaffekaka wrote:
           | Plockepinn in Swedish, approximately "pickastick".
           | 
           | Edit: thought I read it was of Scandinavian origin, hence my
           | comment. But Wikipedia said european origin. Well well.
        
             | quesera wrote:
             | In the US, it was a game called "pick up sticks", and it
             | was tedious and sometimes impossible.
             | 
             | So, this method is well-named at least! :)
        
             | bee_rider wrote:
             | I suspect it was invented the first time a parent dropped a
             | pile of sticks because their bored kids were distracting
             | them. "Ok kids, new game, pick those sticks up as quietly
             | and tediously as possible."
        
         | topaz0 wrote:
         | There are important additions beyond timeboxing, at least
         | according to the post. Notably, reverting your changes if you
         | weren't able to complete the chosen task in the time box and
         | starting over on a chosen subset of that task. I can imagine
         | that part has benefits, though I haven't tried it myself.
        
       | agge wrote:
       | Using a Mikado style graph for planning any large work in general
       | has been really useful to me. Used it a lot at both Telia back in
       | 2019 and Mentimeter at 2022.
       | 
       | It gives a great way to visualise the work needed to achieve a
       | goal, without ever mentioning time.
        
       | agge wrote:
       | There is a great interview that talks about the process and what
       | it is about more generally: https://youtu.be/HbjWOvxJjms?si=5ta-
       | JOyfFLub2yX_
       | 
       | I think there are similar methods, such as nested todo-lists. But
       | DAGs are exceptionally good for this use case of visualising work
       | (Mikado graphs are DAGs).
        
       | mittermayr wrote:
       | While great in theory, I think it almost always fails on "non-
       | existent" testing structures that reliably cover the areas you're
       | modifying. I change something, and if there's no immediate build
       | or compile error, this (depending on the system) usually does not
       | mean you're safe. A lot of issues happen on the interfaces (data
       | in/out of the system) and certain advanced states and context. I
       | wouldn't know how Mikado helps here.
       | 
       | In other words, I'd reword this to using the Mikado method to
       | understand large codebases, or get a first glimpse of how things
       | are connected and wired up. But to say it allows for _safe_
       | changes is stretching it a bit much.
        
         | SoftTalker wrote:
         | Yes, most of the time such spaghetti code projects don't have
         | any tests either. You may have to take the time to develop
         | them, working at a high level first and then developing more
         | specific tests. Hopefully you can use some coverage tools to
         | determine how much of the code you are exercising. Again this
         | isn't always feasible. Once you have a decent set of tests that
         | pass on the original code base, you can start making changes.
         | 
         | Working with old code is tough, no real magic to work around
         | that.
        
         | agge wrote:
         | If you create a graph of what changes are needed to allow for
         | other changes, eventually leading to your goal.
         | 
         | Then by definition you have the smallest safest step you can
         | take. It would be the leaf nodes on your graph?
        
       | yomismoaqui wrote:
       | I recommend reading a classic, "Working Effectively With Legacy
       | Code" from Michael Feathers.
        
       | Illniyar wrote:
       | This is a good method if you are stuck and you don't know what
       | you need to do. It also helps explore a project with a specific
       | task in mind.
       | 
       | It is not very useful in giving you confidence your changes would
       | not cause unexpected side effects, which is usually the main
       | problem working with legacy code.
       | 
       | If you want confidence when working with legacy code, your best
       | bet is to do a strangler fig pattern - find a boundaries for the
       | module you want to work on, rewrite the module (or clone and make
       | your changes), run both at the same time in shadow mode, monitor
       | and verify your new module is working the same as the old one,
       | then switch and eventually delete the old module.
        
         | LoganDark wrote:
         | Boundaries? Module? I laugh.
        
           | nailer wrote:
           | Then your first step is found! Make those boundaries. Isolate
           | dcomponents so you can test them.
        
           | hamandcheese wrote:
           | Replace "module" with "system" - every system has boundaries.
        
             | thfuran wrote:
             | Some of them are notoriously spaghetti-shaped, and that's
             | hard to isolate and replace.
        
           | hinkley wrote:
           | Mikado is really only powerful when dealing with badly
           | coupled code. Outside of that context you're kinda cosplaying
           | (like people peppering Patterns in code without an actual
           | plan).
           | 
           | Refactoring is generally useful for annealing code enough
           | that you can reshape it into separate concerns. But when the
           | work hardening has been going on far too long there usually
           | seems like there's no way to get from A->D without just
           | picking a day when you feel invincible, getting high on
           | caffeine, putting on your uptempo playlist and telling people
           | not to even look at you until you file your +1012 -872
           | commit.
           | 
           | I used to be able to do those before lunch. I also found
           | myself to be the new maintainer of that code afterward. That
           | doesn't work when you're the lead and people need to use you
           | to brainstorm getting unblocked or figuring out weird bugs
           | (especially when calling your code). All the plates fall at
           | that point.
           | 
           | It was less than six months after I figured out the
           | workaround that I learned the term Mikado, possibly when
           | trying to google if anyone else had figured out what I had
           | figured out. I still like my elevator pitch better than
           | theirs:
           | 
           | Work on your "top down" refactor until you realize you've
           | found yet another whole call tree you need to fix, and feel
           | overwhelmed/want to smash your keyboard. This is the Last
           | Straw. Go away from your keyboard until you calm down. Then
           | come back, stash all your existing changes, and just fix the
           | Last Straw.
           | 
           | For me I find that I'm always that meme of the guy giving up
           | just before he finds diamonds in the mine. The Last Straw is
           | always 1-4 changes from the bottom of the pile of suck, and
           | then when you start to try to propagate that change back up
           | the call stack, you find 75% of that other code you wrote is
           | not needed, and you just need to add an argument or a little
           | conditional block here and there. So you can use your IDE's
           | local history to cherry pick a couple of the bits you already
           | wrote on the way down that are relevant, and dump the rest.
           | 
           | But you have to put that code aside to fight the Sunk Cost
           | Fallacy that's going to make you want to submit that +1012
           | instead of the +274 that is all you really needed. And by the
           | way is easier to add more features to in the next sprint.
        
       | Mikhail_K wrote:
       | I usually use the method "shout Banzai! and charge straight like
       | a kamikaze"
       | 
       | Is that the Mikado method?
        
       | brutuscat wrote:
       | For me now days is like this: - try to locate the relevant files
       | - now build a prompt, explain the use case or the refactor
       | purpose. Explain the relevant files and mention them and describe
       | the interaction and how you understand that work together. Also
       | explain how you think it needs to be refactored. Give the model
       | the instruction to analyze the code and propose different
       | solution for a complete refactor. Tell it to not implement it,
       | just plan.
       | 
       | Then you'll get several paths of action.
       | 
       | Chose one and tell the model to write into a file you'll keep
       | around while the implantation is on going so you won't pollute
       | the context and can start over each chunk of work in a clean
       | prompt. Name the file refactor-<name >-plan.md tell it to write
       | the plan step by step and dump a todo list having into account
       | dependencies for tracking progress.
       | 
       | Review the plans, make fixes if needed. You need to have some
       | sort of table reassembling a todo so it can track and make
       | progress along.
       | 
       | Open a new prompt tell it analyze the plan file, to go to the
       | todo list section and proceed with the next task. Verify it done,
       | and update the plan.
       | 
       | Repeat until done.
        
       | dirkc wrote:
       | The things that always get me with tasks like this is that there
       | are *always* clear, existing errors in the legacy code. And you
       | know if you fix those, all hell will break loose!
        
       | jeremyscanvic wrote:
       | Is it possible in practice to control the side effects of making
       | changes in a huge legacy code base?
       | 
       | Maybe the software crashes when you write 42 in some field and
       | you're able to tell it's due to a missing division-by-zero check
       | deep down in the code base. Your gut tells you you should add the
       | check but who knows if something relies on this bug somehow, plus
       | you've never heard of anyone having issues with values other than
       | 42.
       | 
       | At this point you decide to hard code the behavior you want for
       | the value 42 specifically. It's nasty and it only makes the code
       | base more complex, but at least you're not breaking anything.
       | 
       | Anyone has experience of this mindset of embracing the mess?
        
         | 0xbadcafebee wrote:
         | I believe this is called Microsoft Driven Development
         | 
         | (seriously though, this book has answers for you: _Working
         | Effectively with Legacy Code, by Michael Feathers_ )
        
           | fc417fc802 wrote:
           | You misspelled Oracle.
        
         | sublinear wrote:
         | I've never seen code truly get that bad, but I can already
         | think of several problems with that approach.
         | 
         | Do you really know _all_ of the expected behavior you 're
         | hardcoding in? What happens if your hardcoded behavior is just
         | incorrect enough that it breaks something somewhere else? How
         | can you be sure that your test for that specific value is even
         | correct?
         | 
         | I think the better approach is to let things break naturally
         | and open a bug with your findings. You'd be surprised how often
         | someone else knows exactly what's going on and can fix it
         | correctly. Your hacks are not just pouring gasoline onto the
         | fire, but opening a well directly underneath that will keep it
         | burning for a long time.
        
         | niccl wrote:
         | All. The. Time. And I hate it. Imagine giving a customer a
         | rebate based on buggy code. You fix a bug, the customer comes
         | back and wants to check that the rebate was correct that last
         | time. Now you have to somehow hard-code the rebate they did get
         | so that your (slightly less buggy) code gives the same result.
         | But hard-coding has the risk of introducing other errors on its
         | own. Oh yes, and you've never enough time to do things properly
         | because Customers (or maybe Management). A tangled mess of soul
         | destroying lifeblood-sucking code and pressures ensues.
        
       | bob1029 wrote:
       | My favorite tool for trying scary complicated things in an
       | unknown space is the feature flag. This works even if you have
       | zero tests and no documentation. The only thing you need is the
       | live production system and a way to toggle the flag at runtime.
       | 
       | If you can ship your hypothesis along with an effectively
       | unaltered version of prod, the ability to test things without
       | breaking other things becomes much more feasible. I've never been
       | in a real business scenario where I wasn't able to negotiate a
       | brief experimental window during live business hours for at least
       | one client.
        
         | eastbound wrote:
         | You've just resolved a problem I had. I had this problem on a
         | search engine, but I made it as a "v2". And I told customers to
         | switch to v2. And you know the v2 problem: Discrepancies that
         | customers like. So both versions have fans, but we really need
         | to pull the plug on v1. You've just solved it: I should have
         | indexed even records with v1, odd records with v2. Then only I
         | would know which engine was used.
        
         | jaggederest wrote:
         | You can go even further with something like the gem scientist
         | at the application level, or tee-testing at the data store
         | level. Compare A and A', record the result, and return A.
         | Eventually, you reach 100% compatibility between the two (or
         | only deviations that are desirable) and can remove A, leaving
         | only A'
         | 
         | I also like recording and replaying production traffic, as
         | well, so that you can do your tee-testing in an environment
         | that doesn't affect latency for production, but that's not
         | quite the same thing.
        
         | hinkley wrote:
         | Feature flags are like bloom filters. They make 98 out of 100
         | situations better and they make the other 2 worse. When
         | performance is the issue that's usually fine. When reliability
         | is the issue, that's not sufficient.
         | 
         | If you work on fifty feature toggles a year, one of them is
         | going to go wrong. If your team is doing a few hundred, you're
         | gonna have oopsies.
         | 
         | Most of the problematic cases are where the code is set up so
         | that the old path and the new one can't bypass each other
         | cleanly. They get tangled up and maybe the toggle gets
         | implemented inverted where it's difficult to remove the old
         | path without breaking the new.
        
         | nijave wrote:
         | While very powerful, I think it's worth calling out some
         | pitfuls. A few things we've ran into - long lived feature flags
         | that are never cleaned up (which usually cause zombie or
         | partially dead code) - rollout drift where different
         | environments or customers have different flags set and it's
         | difficult to know who actually has the feature - not flagging
         | all connected functionality (i.e. one API is missing the flag
         | that should have had it)
         | 
         | A good decom/cleanup strategy definitely helps
        
           | enlyth wrote:
           | Yep, archiving feature flags and deleting the dead code is
           | usually thing number 9001 on the list of priorities, so in
           | practice most projects end up with a graveyard of them.
           | 
           | Another issue that I've ran into a few times, is if a feature
           | flag starts as a simple thing, but as new features get added,
           | it evolves into a complex bifurcation of logic and many code
           | paths become dependent on it, which can add crippling
           | complexity to what you're developing
        
           | Groxx wrote:
           | Have them emit metrics when it's triggered. You can do a bulk
           | "names X, Y, Z haven't used branch B in >30 days, delete?"
           | task generator pretty easily. Un-triggered ones are also easy
           | to catch if you force all calls to be grep-friendly (or
           | similar), which is also an easy lint to write: unclear
           | result? Block it, force `flag("inline constant", ...)`.
           | 
           | Personally I've also had a lot of success requiring
           | "expiration" dates for all flags, and when passed they emit a
           | highly visible warning metric. You can always just bump it
           | another month to defer it, but people eventually get sick of
           | doing that and clean it up so it'll go away for good. Make it
           | annoying, so the cleanup is _an improvement_ , and it happens
           | pretty automatically.
        
       | w10-1 wrote:
       | Ah, no: incremental approaches only work in already well-formed
       | code.
       | 
       | Poor code requires not coding but analysis and decisions,
       | partitioning code and clients. So:
       | 
       | 1. Stop writing code
       | 
       | 2. Buy or write tools to analyze the code (modularity) and use-
       | case (clients)
       | 
       | 3. Make 3+ rough plans:
       | 
       | (a) leave it alone and manage quality;
       | 
       | (b) identify severable parts to fix and how (same clients);
       | 
       | (3) incrementally migrate (important) clients to something new
       | 
       | The key lesson is that incremental improvements are sinking money
       | (and worse, time) into something that might need to go, without
       | any real context for whether it's worth it.
        
       | csours wrote:
       | This sounds like torture (as written).
       | 
       | Of course, working in a legacy codebase is also torture.
       | 
       | Software development is a hyper-rational endeavor, so we don't
       | often talk about feelings. This article also does not talk much
       | about feelings.
       | 
       | Reading between the lines, it looks like reverting the code is
       | supposed to affect how you feel about the work. Knowing that
       | failure is an explicit option can help to set an expectation;
       | however, without a mature understanding of failure, that
       | expectation may just be misery.
       | 
       | With a mature understanding of failure, the possibility of a
       | forced rollback should help you "let go" of those changes. It's
       | like starting a day of painting or drawing with one that you
       | force yourself to throw away; or a writing session with a silly
       | page.
       | 
       | ----
       | 
       | If someone thinks that they are giving you good advice, but it
       | sounds terrible, then maybe they are expecting you to do some
       | more work to realize the value of that advice.
       | 
       | If you are giving someone advice and they push back, maybe you
       | are implying some extra work or expectations that you have not
       | actually said out loud.
       | 
       | Advice is plagued by the tacit knowledge problem.
        
         | castral wrote:
         | Maybe it is the framing of the step as a "reversion" or "roll-
         | back" rather than "spike" or "prototype" that is causing that
         | sense. Personally, I would never throw away the code I spent
         | time and effort writing just to stick to a systematized
         | refactoring method like this "Mikado." I don't think the advice
         | is unsound, and I have done exactly this many times in my own
         | career, but instead of throwing it away I would shelve it, put
         | it in a branch, write a document about what has been/needs to
         | be done, and write a corresponding tech debt or feature/fix
         | ticket for it with the new and realistic estimate.
        
       | nitnelave wrote:
       | Also known as "Make the change easy, then make the change"
       | 
       | Something to realize is that every codebase is legacy. My best
       | new feature implementations are always several commits that do
       | no-op refactorings, with no changes to tests even with good
       | coverage (or adding tests before the refactoring for better
       | coverage), then one short and sweet commit with just the behavior
       | change.
        
         | hinkley wrote:
         | That's just mostly refactoring in general.
         | 
         | Mikado is more of a get out of jail card for getting trapped in
         | a "top down refactor" which is an oxymoron.
        
         | collingreen wrote:
         | I also do this and try to teach it to others. One thing I add
         | is trying to go even further and making it so the new feature
         | can essentially be a configuration change (because you built
         | the system already in the first steps). It doesn't fit every
         | situation so it's by no means a hard rule but "prefer
         | declaration functionality over imperative".
        
       | spprashant wrote:
       | I d like to hear more about people who have jumped onto large
       | codebases and were instantly productive. I see a lot of emphasis
       | on documentation and comments, but in my experience they get
       | stale real fast.
        
       | woodruffw wrote:
       | I was hoping it was a reference to The Mikado, given that the
       | best way to refactor is with a short, sharp shock[1].
       | 
       | [1]: https://en.wikipedia.org/wiki/Short,_sharp_shock
        
       | nuancebydefault wrote:
       | I've been a few times in a situation where I needed to make
       | significant changes in a huge codebase with lot's of tests but
       | also with a lot of corner cases, on my own.
       | 
       | I've spent blood sweat, tears and restless evenings scrolling and
       | ctrl-f-ing huge build and test logs to finally accomplish the
       | task.
       | 
       | But let's take a step back.
       | 
       | So they assign you to get that done. You're supposed to be
       | careful, courageous and precise while making those changes
       | without regression. There's very little up-to-date documentation
       | on the design, architecture, let alone any rationale on design
       | choices. You're supposed to come up with methods like Mikado,
       | tdd, shadowing or anything that gets the job done.
       | 
       | Is this even fair to ask? Suppose you ask a contractor to re-
       | factor a house with old style plumbing and electricity. Will they
       | do it Mikado style, or, would they say - look - we're going to
       | tear things down and rebuild it from the ground. You need to be
       | willing to pay for a designer, an architect, new materials and a
       | set of specialized contractors.
       | 
       | So why do we as sw engineers put up with the assignment? Are we
       | rewarded so much more than the project manager of that house who
       | subcontracts the work to many people to tear down and rebuild?
        
         | phito wrote:
         | If you're paid by the hour, then does it really matter if you
         | have to refactor stuff? If it takes a long time to do then
         | it'll be more expensive for your employer.
         | 
         | Does the project manager get paid more by the hour to refactor
         | a house than to build one?
        
       ___________________________________________________________________
       (page generated 2026-03-02 23:01 UTC)