6 ms·
As Martin Fowler correctly explains, YAGNI is primarily about end-user features, not the flexibility of the code and the toolchain. If you don't keep your tech
by sz4kerto 8y ago
As Martin Fowler correctly explains, YAGNI is primarily about end-user features, not the flexibility of the code and the toolchain. If you don't keep your tech stack (code, infrastructure, people) flexible, then iterative development becomes impossible. The assumption behind YAGNI is that 'you can't predict requirements accurately'. Which is true, so don't build them ahead of time. However, the same assumption requires you to be flexible, modular, etc.
Specific example from TFA: distributed app vs. large server. A distributed app might give you better scalability, which you might not need because large servers are cheap. However, a distributed app also forces you to think about state, so if you screw something up and have to fix it middle of the day, you can BG deploy the app instead of having a 4 minute outage. It forces you to modularize the logic and think about interfaces, so if you need to add, remove or change functionality then it'll likely be faster.
Etc.
What this article forgets to say is: "Stop future-proofing your software if you are still looking for a good product-market fit. As soon as you know what your business is going to be, make sure your technology stack is flexible."
- fsloth 8y ago"As soon as you know what your business is going to be, make sure your technology stack is flexible." As a form of devil's advocacy - Why? Just improving the codebase under the hood won't bring any new users in the initial market segment where your product-market fit is proven. Focusing on marketing, will...
- sz4kerto 8y agoGrowth comes with technical issues. As a rule of thumb, 2 orders of magnitude increase in traffic requires significant refactoring or change in architecture. 1 orders of magnitude in team size requires new forms of management. Etc.
- mikekchar 8y agoThis is a good question. The answer is so that we can amortise the expense of feature development. Side note: This includes bug fixes. A bug is just a feature that you expected already worked. A bug is different from a "software error", in that such errors will impact you when you next work on the code. It is often possible to fix bugs without fixing the underlying software error and vice versa. Having a "flexible" code base (to me) means being able to change the code easily. That change could be to add new functionality, or to refactor the design, or both. "Flexible" (to me) does not necessarily include having actual facilities to do anything. As an example, your normal "Hello, world!" C program is very, flexible. It's easy to modify. A similar "Hello, world!" written using a large framework is less flexible because you have more constraints on what you can do, and how you can change the design, even though it has more facilities. Adding flexibility does not mean adding more facilities to the code. Often it means removing unused facilities from the code in order to make it simpler. IMHO, this is the true distinction with YAGNI. We remove YAGNI code in order to increase the flexibility of the code. Sometimes people are tempted to write wonderful elaborate designs for their code in order to ensure that it's easy to do something in the future. This is a classic case of YAGNI. We want to remove that code in order to increase flexibility (because often new requirements move in a direction that is opposed to the original design). However, I sometimes wish that there was a cute acronym for "I Actually Need It Now" (suggestions welcomed :-)). In this case, let's say we've written some code and it's awkward, but it does the trick. A little while later, we end up doing the same thing. It's relatively easy to copy and paste our previous awkward code. Then we have to do it again. In those cases, you've found that you do need it, and you're adding complexity to your code base every time you copy and paste the same awkward code. It's beneficial to build a nice system to make it easy to do it. Next time, not only will it be quick to add, but it won't add unnecessary complexity to the code base. You've probably heard of the "rule of 3". Of course, it's a rule of thumb, so you have to use your own judgement, but the idea is that if you have to do it once, then just do it. You can't generalise what problem you are actually solving because you just did it once. There's not much sense in agonising over the "ultimate" design, because you are likely to get it wrong anyway. But by the time you've done it 3 times, you've got a pretty good idea of where this is going. At that time you should invest in an appropriate solution. The solution will likely increase the architectural complexity, but it will simplify the interfaces and will slow down the increase in overall complexity. Again, 3 times is a rule of thumb. Sometimes you know right away that you need something more complex. Sometimes it takes you a lot more than 3 times before you can really wrap your head around what direction is best. So a better rule is: delay making design decisions until you have enough data to answer your questions. And to roll it all back to the beginning, YAGNI happens when you make decisions before you have the data to support your decision making. This can be both with user features and with design. By delaying these decisions until we have good data, we keep our options open and the system remains "flexible". But if we do not make the decisions when we do have enough data, the system becomes complex because it lacks cohesion. We have to act promptly to address those issues, or else we will have a very uneven development experience. "Hard" things may take a short amount of time, because we didn't build all the infrastructure we needed. "Easy" things may take a long amount of time because we suddenly need to build the infrastructure that we didn't build before. And that's what I mean by "amortising" the expense of feature development. We don't spend a lot of time up front to build a complex, but inflexible system. However, we spend time intermittently, to maintain flexibility and to keep the overall cost down. Even more importantly, businesses depend on the predictability of development. By maintaining a high degree of flexibility, we allow the cost of solving problems to approximate the complexity of the problem. Without having a flexible code base, the cost of solving the problem is often dominated by the complexity of the code base instead of the complexity of the problem. This leads to management not being able to "trust" development and often leads to project cancellation. Another aside: I often say that upper management is the most dangerous part of the team for a project because they are the ones that cancel the project. If they do not have a good feel for a project, they may very well cancel it for extremely poor reasons. Thus, aligning the project to the expectations of upper management is one of the most important parts of software development. I personally don't like doing development work that isn't tied to direct economic value. This includes refactoring. Refactoring may contribute to indirect economic value (by reducing development time int he future), but it is risky work. As I mentioned in the previous paragraph, upper management depends on the predictability of development. If you randomly (from their perspective) say, "We're going to do some feature sized work, but it's not going to result in any different functionality", it separates them from the logic of the development. They may tolerate it, but it makes planning difficult. Instead, I prefer to spread that kind of work out and to "same size" the work. This is harder to do from a design perspective because you have to find small goals to achieve and to work piecemeal. You need to have good communication with your team and good buy-in for the process, so that each team member takes the appropriate opportunities every time they touch the code. In short, it makes the development process more difficult, but the advantages are many. In that way, I do exactly what you suggest: I only work on code that is adding customer value. But I intentionally take a little bit of extra time in each story to find ways to improve the flexibility of the code. I also spend time each story to add facilities (which I've called "capabilities" previously) where appropriate. Occasionally you get the, "OMG, we need to do a big refactor", but if you have been diligent in maintaining flexibility, then it should limit the amount of time that is necessary to move in the correct direction.
- dominotw 8y ago> If you don't keep your tech stack (code, infrastructure, people) flexible, then iterative development becomes impossible. Thats the crux of the problem. Different people have different definitions of flexible. How are you defining it?
- rhizome 8y agoWell obviously it's going to be in their code style document(s) given to every programmer during onboarding.
- BurningFrog 8y agoIn this context it means that it's easy to make changes. As in add new functionality.
- mlthoughts2018 8y agoBut you can’t make it equally easy to add in every type of functionality. For any type of extensibility design you bake in, some other types of extensibility now become harder. So “making it flexible” hinges entirely on whether or not you can correctly forecast exactly which types of flexibility you’ll need. But this is no easier, and often harder, than predicting how end user requirements might change in the first place!
- BurningFrog 8y agoWhat it means in practice is no extensibility baked in at all. Rather it is very modular design, that is very simple, bordering on dumb. This is all very much easier said than done. "Simple ain't easy".
- mlthoughts2018 8y agoExactly. The same “end user” problems recursively repeats. You just think of other (or future) developers as the end users, and those actual user requirements you can’t predict, that ends up meaning you also cannot predict what the developers flexibility requirements will be either. You end up “making your codebase flexible” in all the wrong ways by introducing abstractions, interfaces, and extensibility designs that end up being the wrong tool for the job once the future requirements are known. There may still be a simplistic, high level where you can have certainty about how to factor code. E.g. separating front-end and back-end, using tests as scaffolding for helping verify deployments and facilitate changing code, using a few simple tricks here and there to reduce boilerplate or make sections of code reusable. It’s totally fine to pursue these optimizations, especially if they are part of a healthy backlog process to prioritize them. But the problem creeps in when principal developers or “philosophizing architects” take it as a goal unto itself and begin trying to mandate it all the time, especially with the anti-pattern where huge architecture discussions become synonymous with routine “just get something done that works and refactor later” code reviews.
- golemiprague 8y agoI don't know, some features seems to come up in almost every project, for example roles and permissions, even when people swear on their mother life that they don't need it within half a year they will come and ask for it. So might as well slap it in there right in the beginning.
- sseth 8y agoThis is a good point. But it only goes so far. Ultimately, even code and toolchain flexibility comes at a cost - for example, should you go for a non-relational database because of potential future scalability needs, or cross that bridge when you come to it? Or should you make your code internationalizable, even though the first release is in English only? The right answer may depend on the probability of the business need occuring in some foreseeable time frame. Unfortunately, the OPs point is too black and white, where the reality is much more grey. In my view decisions in a project need to be subject to a cost-benefit trade-off, and future-proofing is no exception.