4 ms·
A Plea for More Mikado
- ColonelPhantom 3y agoDoes anyone use this method in practice? How well does it work? I can imagine it not working well in some cases, for example when a failure can only be resolved by some new thing (e.g. when a function was replaced by some other function). There also seems to be an implicit assumption that the tests are 'sufficient', whereas I'd wager that the whole reason a revert is needed is the fact they are, in fact, insufficient. The article even says so: > Obviously, the Mikado Method cannot work if you don’t have a good and highly reliable automated test suite. But if you have a good test suite, then there should be no problem shipping the big change in one go! I imagine the mikado method can still work though, because when shit does hit the fan, you can just downgrade to the previous version while keeping most of the preliminary changes? And that should then reduce the cost of the revert.
- p_l 3y agoArguably, the Mikado method is also a way to make the hundreds of small changes that will ship as one big change
- adastra22 3y agoI've never heard of this until today, but it is more or less the strategy that I've been following for a few years now. I can say it works pretty much as advertised here. I've done some major refactors by splitting into atomic changes like this, and although it takes a lot of time and effort, the end result is comparable to the work put in. What I mean is that a big refactor might take 9 months to do atomically whereas it could have been done in 3 months as a massive single PR. However I guarantee you we'd have spent 6+ months cleaning up unexpected issues that show up after merging the giant PR. In the atomic commit approach what you get at the end of the 9 months is a solidly engineered product without those issues. I will say that on a team, issues that you encounter are that it is very hard to keep track of these work-in-progress refactors that are getting in piece by piece, and it can cause rebase hell for other committers if there is a lot of refactoring involved. Splitting one giant refactor into 3 smaller refactors is better for the product, but represents 3x the work for other team members who have to rebase all their changes every time something is merged.
- hitchstory 3y ago>What I mean is that a big refactor might take 9 months to do atomically whereas it could have been done in 3 months as a massive single PR. My experience with these 3 month-long PRs is that they have a tendency to not get finished. Then they get stale. Then they get abandoned. Then somebody starts another one. In one case it was finished, but the CTO was just too afraid to deploy it because it was big and it might break something. So while we were waiting for the right deployment window it got stale, meaning more conflict fixes.... I also think most people overestimate the amount of extra work involved in breaking up a refactoring. It's usually no more than ~20% more coding, and that extra work almost always pays for itself by reducing deployment risk.
- Hamuko 3y agoWe automatically close PRs after a couple of months of inactivity and basically no one ever reopens them ever after they're closed.
- tome 3y ago> What I mean is that a big refactor might take 9 months to do atomically whereas it could have been done in 3 months as a massive single PR. However I guarantee you we'd have spent 6+ months cleaning up unexpected issues that show up after merging the giant PR. Yes, one of the lessons of process engineering a la Deming is that the important thing is to get the process under statistical control, which typically means minimizing the variance inherent in a process, rather than the mean. Doing a big refactor might minimize the mean time to completion, but it will typically have a long tail which occasionally bites you and costs you dearly. The approach that minimises the variance might take longer in mean but generally when it's done it's done. Fewer unexpected surprises.
- hitchstory 3y ago>Does anyone use this method in practice? I used a very similar method to upgrade Django from a very low version to a very high version where I would periodically: 1) Upgrade the dependencies. 2) Find something that was broken and fix it so it worked on both versions - usually with an ugly if statement. 3) Merge to master and deploy. 4) Repeat 2 days later when I had another spare half hour. Because it was a pressure cooker environment, the testing was thinner on the ground than I would have liked but mostly because this was an enormous change this whole process took about a year. I actually left the company before I finished off the upgrade and I thought this work had been abandoned. However, my coworker picked it up after I left and finished off the upgrade in the same way. Before I joined there were several aborted attempts to do big bang upgrade on the same thing - stale branches that just died by optimistic people who repeatedly thought they could do it all in one go. >But if you have a good test suite, then there should be no problem shipping the big change in one go! No, there is. If the change is large enough this is still a horribly bad idea - especially if there are structural changes to data involved. You might be able to make bigger chunks than you would otherwise, but you should still drip-feed large refactorings into production because something can always go wrong. The fewer changes you make at once, the lower chance something will go wrong and the easier it is to isolate and fix the problem if it does. If you are starting out with a good test suite (something I find almost never exists on a project I'm joining), you should be upgrading everything via small increments and very frequently - negating the need for using this strategy on most upgrades.
- Archelaos 3y ago>> Obviously, the Mikado Method cannot work if you don’t have a good and highly reliable automated test suite. > But if you have a good test suite, then there should be no problem shipping the big change in one go! The author is referring here to unit test suites. These cannot catch every problem that may arise due to refactoring when integrating the refactored code with the rest of the code base. His method basically means that we should look for incremental integraton tests to catch such problems early.
- benjiweber 3y agoThere are lots of other benefits to many more much smaller steps as well as safety https://www.geepawhill.org/2021/11/16/mmmss-the-intrinsic-benefit-of-steps/ https://www.geepawhill.org/2021/11/16/mmmss-the-intrinsic-be...
- murkt 3y agoI’ve used it many times with great success. Not in a dogmatic way — I don’t usually delete my “code in progress”. I would rather cherry-pick standalone changes to new PRs, and then rebase. Or keep unsuccessful code in a branch that I can look at, when needed.
- countWSS 3y agoI use this(i don't call it Mikado or use the idea), its much easier than rebasing your development branches on whatever was last commited. Breaking things down into simple components is possible even for big things, just add them as pieces of 'alternative path' and when its ready switch to the 'alternative path' for testing. The 'old path' code then can be removed later, also piece by piece to avoid breaking stuff.
- tyleo 3y agoI use a similar strategy to this… much less formal but I’m excited to try this formal write up. My strategy was more like: 1. Start making a big change. 2. See what breaks. 3. Move the big change to another branch. 4. Fix one broken thing and PR to main. 5. Rebase the big change back on top of main. Essentially trying to break off chunks of the larger problem in small commits. I’ve had about a 90% success rate with this strategy and when it works developers really appreciate reviewing the smaller commits than one monster change. I once heard that, “complex systems are built from simple systems,” and started to view my job more as identifying and working on the simple things to let the complex stuff fall out rather than attacking the complex stuff directly. Edit 1: one other thing I will add is that this strategy has the huge benefit of producing many smaller tasks that more of a team can work on and commit to main rather than having one engineer do the whole refactor or having folks working out of a half functional refactor branch with nonstandard git workflows. Edit 2: I've used this strategy in codebases with and without automated/unit testing. Where automation didn't exist, I've used dedicated QA staff. QA also appreciate testing small changes that impact part of a product rather than 'retest the entire product'. In environments where productivity is constrained by QA turnaround time I've prepared changes in a staging branch and keep having QA test the latest staging branch as soon as the previous has merged.
- jonhohle 3y agoYeah, that’s my method as well. Start big and even fix nits along the way. Anything that’s in the way is fair game as long as all of the tests are passing. As fundamental, atomic changes are discovered, peel those off into separate commits and PRs justifying their current and future benefit. Rebase those changes on the big branch and repeat until all the changes in the big branch are merged. I’ve found that having a lot of small commits in my dev branch also helps makes rebasing easier. When there are conflicts, it’s easier to reason about what atomic set of changes they might affect than the whole wad. It also makes peeling off those changes into their own branch easier if things get to hairy.
- tyleo 3y agoTotally agree! When I work in this way I'll rebase all the nits up top where possible. I like to try to get the history to tell a sort of 'story of development'. Though this can be time consuming so I wouldn't fret about it if it becomes burdensome. When it works, this has a nice impact of dividing the PR into: 1. Here is what we were trying to do. 2. Here is all the stuff we also cleaned up which is somewhat unrelated but in the hot path of these other changes.
- JackMorgan 3y agoIn 2021, I ran a two-team, two-codebase detangling project with 20 engineers. Both codebases were writing to the same database, but one was the leader for some fields and the other was the leader for the rest. It was causing serious production issues in a highly regulated field, so we fixed it as fast and accurately as possible with almost no downtime. The very first thing was building in automated error detection processes so we could have product staff catching and fixing errors live. We split the work into six releases over six months. Separating the two systems required a totally different architecture with a significant amount of code changes in the one system. We didn't exactly use this method step by step (I hadn't heard of it). We thought logically through all the steps, and then reversed them. There were spike branches that we used to gather all the steps, and then we did delete them. But we only used maybe 4 or 5 spike branches total. Unit tests didn't drive anything - these changes were more system-integration level, so there weren't existing unit tests we could use. In the end, it worked, we made a major overhaul to the one system and updated the second to now be the leader on all. We built an API on the second system, so it could be called by the first when new records needed to be created. So I'd say, conceptually this method is what we used, but really none of our to-do list was generated from unit tests. Perhaps we could have built out a ton of tests and separated it into a release every week or every day, but we felt comfortable with the monthly releases. That helped because the QA process was pretty long (a lot of features in a regulated industry). Each release they were testing for at least a week. That being said, we built out a lot of unit tests to complement the changes, and I don't think QA found any issues in any of the six releases. As the author described it, Mikado method is recursive. You find a to-do list on the first branch, then possibly more things on the second branch, and more things on the third. We only did one "loop", maybe two, where the author might do many. I got this by thinking about the Kent Beck quote "first make the change easy, then make the easy change".
- posix86 3y agoI don't think having extensive tests makes it possible to change anything. Much of the work of a programmer is splitting up problems in to chunks that can easily fit into our very limited bulbs. This article highlights that there are two aspects to this; one is the one widely known, which is using appropriate abstractions to reduce complexity of any isolated part of the program; but the other part is keeping the changes at a manageable scale. To me this sounds like a very valid and potentially underapprechiated part of working on code with other people involved.
- iainmerrick 3y agoI often use an approach like this, although I've never tried to pin it down formally and I'd never heard it called "Mikado". I really like this writeup! When working on a big gnarly feature, I'll start by hacking it in and seeing what breaks, or what prerequisites are missing. When I find a piece that's standalone and definitely needed, I'll move that to a clean branch and get it in. I usually do blow away the messy development branch and restart from scratch, often multiple times. I generally find the important and time-consuming part is finding out what's needed rather than the actual code itself. The final code is usually small and doesn't take long to rewrite. That matches the OP's experience pretty closely. I'm a firm believer in Gall's law (https://en.wikipedia.org/wiki/John_Gall_(author)#Gall's_law) https://en.wikipedia.org/wiki/John_Gall_(author)#Gall's_law): A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system. Doing things step-by-step is the only way to build something that works. And while you can do that iterative development in a branch, it's almost always better to commit those iterative steps to the trunk as soon as possible, so you never have to do that massive risky merge at the end.
- dclowd9901 3y agoParts of it seem familiar. But rather than the weird cyclical sort of methodology, it's more like we create a framework for being able to migrate changes piecemeal without requiring a big change. The aim of small incremental changes remains the same but without all the silly redundant work.
- al_borland 3y agoThis is my first time hearing the term, but I’ve often done something kind of like it when dealing with upcoming changes from systems owned by other teams. Rather than make a big change that requires I’m online and deploy my code at the same time they deploy, I make my code so it supports the new and old system. They have their deployment window and I get to not care about it.
- wodenokoto 3y agoI don't understand the example. How do you fix the issue in the test, when the issue only exists in the version 7 branch, which you deleted?
- ColonelPhantom 3y agoI think the point is that if e.g. v7 removes a certain method, but the replacement is already available in v6, you can simply keep using v6 while updating your code to use the new method. By doing all this work on top of v6, upgrading to v7 becomes as simple as 'flipping the switch', and reverting it if issues do arise also becomes trivial. But yes, I can definitely imagine cases where trying to apply this method leads to a chicken-and-egg problem.
- alias_neo 3y agoLike the person you replied to, the example makes no sense to me. I don't program in Rails, but, in the languages that I do/have I don't recall a situation where it was _likely_ that a function or API that replaces something deprecated in an older version, was already available in that older version. The only case I can think of, that happens regularly, is that something would be deprecated and marked as such, with a replacement available at the point of deprecation. That deprecated usage should be replaced before it is removed; and if we're talking about skipping multiple major versions over a long period, the replacement likely didn't exist in the older version, so this method still wouldn't work.
- twic 3y agoThe replacement is almost always available at the time of deprecation. Old things get deprecated precisely because there is a replacement.
- alias_neo 3y agoOf course. I think you and the other commenter missed my point. If you go from version 4 (say), to version 7 (say,several years later), something could have been deprecated in 4/5 and removed entirely in 6/7, you've missed the transition period between the thing being deprecated and it being removed entirely. There's no way you're going to use the method in OP to "fix" all of the problems you'll encounter going from 4 all the way to 7 without just going to 7 and fixing everything that's broken.
- codeulike 3y agoWill go down well with people looking to make TDD even more performative and uneconomic
- JackMorgan 3y agoHaving used both tools to solve big problems in highly regulated fields, I strongly disagree with this take. Certain kinds of projects benefit from these tools. To say they are "uneconomic" is not only ignorant but condescending. It comes off like a carpenter calling a welder "uneconomic" because the welding machine takes longer to start and cost more than a hammer and nails.
- quickthrower2 3y agoI think smaller commits and fewer long lived branches are good. Feature flags are good. Breaking stuff down logically is good. But this example seems inefficient. Keep that original failing branch in a seperate clone to refer back to. You can even pull down the fix commits back to it to observe the test count improve gradually! And with dependencies certainly in .NET and Node and probably Ruby too there are dragons in the details. Let the compiler and/or tests do that heavy lifting rather than a tree if postit notes. For example you assume you know upgrading gem A will need changes XYZ but I bet ya there are gotchas related to when you upgrade B and C along with A. Picking the granularity level takes a nose for it but I wouldn’t waste time planning just start chipping away at the problem to help get a lay of the land. It feels like this method is like planning your walk to the shops and creating a survey of dogshit on your path. Just get walking and look where you walk!
- gwd 3y agoA more specific implementation of "Make the change easy, then make the easy change". Another tool that's useful (if you're doing email-based development at least) is always floating minor fixes and refactoring to the beginning of your series. Then the fixes and refactors can be checked in as they're reviewed, such that the final merge is much smaller.
- deleted 3y ago[deleted]
- manicminer 3y agoI use this method and became converted after a long lived refactor branch caused merge hell, delayed a project for many months and caused all manner of bugs that we were fixing for a long time afterwards. My very wise and experienced CTO said to me something along the lines of: "if you don't think it's possible to break a big refactor down into small steps, then you're probably not thinking hard enough". That has stuck with me. I wrote a blog post on atomic development a while back which references the Mikado method. https://tomsouthall.com/blog/atomic-development https://tomsouthall.com/blog/atomic-development.
- Sesse__ 3y agoI once had a coworker working on a refactoring branch for one and a half _years_. Just merging with the main branch after they had things working took weeks. Especially as we had switched coding styles and reformatted all the source code in the meantime… (It eventually shipped, but I'm fairly certain they could have saved themselves quite a bit of pain by landing it in smaller chunks.)
- butshouldyou 3y agoI have a colleague who performed a major migration of the linting tools we used. TSLint to ESLint. Of course, they also fixed all the new rules changes. All in all, took them roughly thirteen months. I'm certain that they would have had less trouble merging their changes in if they did so in much smaller chunks.
- rollulus 3y agoWhen I think of playing Mikado I think of altering pieces that (you hope) are isolated, in a constant fear that the whole mess collapses — because there was a connection you didn’t notice. Sounds like the opposite of good software engineering.
- atemerev 3y agoThis is awesome, particularly the bit about deleting your code when you are stuck, and starting anew. I immediately remember hundreds of issues that could be fixed much earlier, if I wouldn't have persisted with the first draft of the code.
- NKosmatos 3y agoLooks interesting, but I thinks it fits in specific projects. There isn't a "one size fits all" solution in our profession (software engineering). Initially I thought this was a plea for more Mikado chocolate sticks, a sweet snack popular in Europe :-)
- cmiller1 3y agoInitially I thought this was going to be a plea for more opera comedies.
- tomstuart 3y agoMike Dalessio, the maintainer of Nokogiri, gave an excellent talk about this at Rails World 2023: https://youtu.be/USPLEASZ0Dc?t=939 https://youtu.be/USPLEASZ0Dc?t=939
- zubairq 3y agoI work like this... small atomic changes which can always be shipped. I never knew it was called the Mikado method!
- lupire 3y ago20 years ago it was just called Agile. 40 years ago it was just called programming.
- BlueTemplar 3y agoYeah, and project management 101 seems to have a good explanation why more iterations are better in this case : you want to be more Agile when the project is of a more experimental nature, and the larger the refactor, the more likely you are to run into one of these "unknowns"...
- pimlottc 3y agoThis is something where git rebase comes in handy: - Start working on a breaking change - Realize you need to fix something else - Commit your WIP breaking change - Make and commit the fix - Rebase your changes to move the fix onto HEAD (before your WIP commits) - Push the fix - Continue working on the breaking change
- TylerE 3y agoWhy is all that ceremony better than just doing a git stash?
- pimlottc 3y agoThat's another option, sure! Personally, I tend to make chains of small commits as I'm working, so keeping things in normal commits makes more sense to me. I like having everything clearly linked to the branch I'm working on. It's easier for me to forget what's in a stash and what it relates to. To me, stash just seems like a weird non-standard version of a normal commit without any major benefits.
- TylerE 3y agoI’ve used a feature branch workflow for years, so stashes are localized. Basically except really giant refactors (which are rare, it’s a 20 year old codebase) you just fork off a feature branch. Master really only exists for merges and the occasional get it out right now one liner. I say all this with the caveat that our internal scripts merge master into the feature branch on commit, so it forces you to deal with diff rot early and easily.
- flerchin 3y agoThis is kinda how we handle kubernetes upgrades. Dozens of MRs to change all the little things, then the final MR to upgrade the cluster.
- Tao3300 3y ago> Obviously, the Mikado Method cannot work if you don’t have a good and highly reliable automated test suite. That should have been at the top in a disclaimer.
- JackMorgan 3y agoI'm not sure I agree. I have used a similar technique on a project without any unit tests. You can still break down a feature into it's smallest parts without unit tests.
- tome 3y agoYeah, I mean, it's kind of true, but only because no method is going to help you if you don't have some automated way of reassuring yourself that you're doing the right thing.
- boxed 3y agoI did something very similar when moving a big code base from python 2 to 3: https://kodare.net/2018/02/09/moving-a-large-and-old-codebase-to-python3.html https://kodare.net/2018/02/09/moving-a-large-and-old-codebas... Under the heading "experimental branch".
- 33a 3y agoI'm never gonna write code like this, but maybe one day I can bully an LLM into doing TDD until cries.
- throw4847285 3y agoI've gotta know, why is it called the Mikado Method? Is it a reference to the Gilbert and Sullivan operetta? The notion of a Japanese emperor?
- croisillon 3y agothe picture is a clue: it is a reference to the Mikado game, where you shall remove one stick after another, without making anything else move https://en.wikipedia.org/wiki/Mikado_(game) https://en.wikipedia.org/wiki/Mikado_(game)
- throw4847285 3y agoHuh. My brain skimmed over that image and assumed it was a bow and a quiver of arrows. That makes a lot more sense. Thanks!
- Dan42 3y agoIt's the first time I hear "Mikado" but it's pretty much the commit policy I established for work, and we call it micro-commits. http://lucasr.org/2011/01/29/micro-commits/ http://lucasr.org/2011/01/29/micro-commits/ https://mrcote.info/blog/2017/12/04/more-lessons-from-mozreview-mozilla-and-microcommits/ https://mrcote.info/blog/2017/12/04/more-lessons-from-mozrev... https://dev.to/rpalo/plan-your-commits https://dev.to/rpalo/plan-your-commits The article focuses on refactoring and migrations, but it works also for regular development. One rule of thumb is: write a one-line commit message, and then write the code described by the commit, and just that. Another rule of thumb is that refactors should go into their own commit. So while you are coding according to your one-line commit message, if there are small refactors needed along the way, commit them separately. Fix whitespace? One commit. Extract a method so you can re-use it? One commit to extract, then use the extracted method in your next commit. Having small atomic commits makes code that is much easier to review, and much easier to test. I think it's similar to unit testing: we change how we write code in order to make it unit-testable. This is for code review: we write code with micro-commits in order to make it code-reviewable. This method is pretty much incompatible with squash-and-rebase. Squashing throws away all the useful information of micro-commits. Instead, just merge, and the merge commit itself is equivalent to the squashed-rebased version. > Obviously, the Mikado Method cannot work if you don’t have a good and highly reliable automated test suite. I disagree. Of course it's always better to have an automated test suite. But in the absence of one, the only thing you can rely on is thorough code review. And with micro-commits you have a much better chance of spotting problems if the code-diff you review contains only a small self-contained atomic change. Code-reviewing a large squashed commit is all but impossible.
- tome 3y agoI love this thread! I'm discovering that there are dozens of us who like working in small commits :) My question to you all is where do you hang out? Where can we do more discussion on software development practices like this?
- AlbertCory 3y agohow can you write about something called The Mikado, talk about taking things out, and not say, "They'll none of them be missed." ?
- pavon 3y agoI didn't really follow the benefit of deleting your WIP despite the author being emphatic that it was important. In my view, if a change A I'm working on requires me to detour and make some change B, then a mandatory requirement is that change B really does work for change A. And not just I think it will work based on my memory of what I was doing, but I tested it and it really does work. My ability to predict all the details and impacts of a change are fallible, otherwise this unexpected detour wouldn't have happened to begin with. I guess that does point to a benefit - it decreases the desire to hold off on merging all the incremental changes until the full refactor is done. With the WIP there will always be one loose end to tie up before it is complete and you are really sure all the changes worked together. Without it, all you have is that the individual change didn't break anything so might as well merge even if there might be rework in the future.
- ricardobeat 3y agoI had the impression that is a crucial aspect of the method. By starting from scratch each time, you avoid creating a chain of changes that may not be the smallest / most efficient, because you didnt have the full context when you made the previous change. And if turns out that that was, indeed, necessary and the right thing to do, it’s still fresh in your mind to reimplement. (I have not tried this method myself yet)
- Jach 3y agoI read the book (which is only 200 pages, a lot skimmable, and free online even (edit: used to be, now parts on the manning site are garbled)) and liked it. Presented it to some coworkers once, and described how I used the method pretty successfully for a few actual work items. Because the work environment used Perforce, not everything was done as micro commits, but still. One task was small, it was to delete a secret token from a hardcoded XML config file and instead have it be consumed by calling out to a new secrets server some other team had setup. Nodes were things like "Read the doc for this new service", "Unblock the MVN issue preventing setting it up locally", "Set it up locally", "Store the token locally", "Store the token to the production cluster", "Identify all consumer(s) of the token", "Update the consumers to pull from the server", "Remove the token from the XML". Another slightly larger task was to refactor/fix a migration framework someone who had left the company wrote to migrate version-by-version doing all components at each version at once, instead of component-by-component doing all versions for just it at once. Without Mikado it probably would have been one giant commit, with Mikado it was 3 with everything working at each step. (If we used git probably could have been more.) And the migration bugs basically went away after that, so that was nice. The method is something I think about when things start to feel uncertain or hairy, but I try to avoid such situations, so I haven't used the method very often in the last few years. Especially the formal version. Informally, for my solo work (it works outside of coding too) it's more like a more structured TODO list -- it becomes a TODO graph, with an obvious do-this-next item (one of the leaf nodes with no prerequisites). It can help in planning and breaking things down as well, even if it's not used for the implementation work itself. A dependency graph alone is useful for a lot of stuff. The biggest objection is always the revert step. It's similar to TDD's red-green-refactor idea. If your tests start breaking during a refactor, you didn't correctly refactor, so rollback and try again. They should only break when you write them to fail in the red stage. I don't particularly like TDD, and don't practice it, but the idea of going back to a known-working state in the face of issues is really vital rather than trying to charge forward. Yes, in a lot of trivial cases the blocker isn't worth a node in your graph and you can get away with plowing through and fixing the red squigglies in the IDE one by one and be fine, but you should resist the temptation if you're giving the method a fair go and if past experience doesn't exactly support your hubris. In practice reverting usually isn't a big deal. The changes you're making at any time tend to be pretty small, undoing them because you learn it's not time to make them yet isn't usually a great loss. And when you come back to it after resolving the prerequisites, doing it again doesn't take much time, because every step is supposed to be pretty simple, and you already did something like it before. (You can of course save more complicated bits off in a scratch file or local branch to refer back to later, just that it's more the exception.) Writing the code also isn't necessarily the greatest part of overall development time. Remove one unnecessary meeting and you buy yourself time to throw away and redo quite a number of changes. And the learning of the whole system you'll get is probably more valuable too, and is reinforced when you redo things a bit differently again after resolving prerequisites. Scratch work to throw away is part of work, anyway. Like, when I'm learning a new codebase, often I make edits to it that inline a bunch of stuff because so many codebases are messes of function calls going all over the place (often for good reason revealed later) and there's only so much jumping around my brain can handle before I start losing the overall context or struggling with the debugger's limitations. I undo it all later, but the learning part is what's valuable long term. Sometimes you need to explore options. Some wisdom from Carmack: "Focused, hard work is the real key to success. Keep your eyes on the goal, and just keep taking the next step towards completing it. If you aren’t sure which way to do something, do it both ways and see which works better.” (https://slashdot.org/comments.pl?sid=25551&cid=2774808 https://slashdot.org/comments.pl?sid=25551&cid=2774808) Also http://sevangelatos.com/john-carmack-on-parallel-implementations/ http://sevangelatos.com/john-carmack-on-parallel-implementat...