Live data from Hacker News

Stop future-proofing software

medium.com

41–50 of 63 posts

Re: Stop future-proofing software

#41

This article reminds me of the adage about ops people which goes, "You know you're doing a great job in operations when nobody thinks you do anything." The problem with taking the experience that "future proofing" is never worth it, is that it suffers from survival bias, which is to say that when you did it and it made the future event a non-event, it didn't even register on your brain. If you practice more mindful e…

So, to me, "future proofing" has some very specific connotations. It doesn't mean building for eventualities that you know will happen and already know how to build for (though, if it takes extra time to do so, you do need to be mindful of the cost of delay), and it doesn't mean things like writing well factored and loosely coupled code (within reason) so that you get a system that's generally easier to modify.

It means going out of your way to try and handle scenarios that you don't know will occur, or whose details you don't really understand yet, with clever one-off facilities.

E.g., I've come into a couple projects now, where I'm supposed to add in some new feature, and I discover that the original author has thoughtfully added a bunch of extension points to try and help me with that. Both times, I also discovered that building in those extension points accounted for a large amount of the pre-existing complexity in the code, even though they weren't actually being used for anything yet. And both times I also found that none of them actually addressed my needs, so all that extra effort and complexity was a waste. One time I was unfortunate in that the existing code was of the "600 line functions and a whole mess of mutable state and temporal coupling" variety, so changing it would have incurred excessive risk, and I opted for just jamming my square peg into that round hole. The second time it was well-factored code, so it was easy enough to rip the round holes out to make room for square ones. But it would have been even less effort if I could have skipped that cleanup step and just jumped straight to adding the extension points.

Re: Stop future-proofing software

#42
like so many things written about software, this ignores everything in the process that isn't a computer program. Future-proofing is a natural consequence of a management style that doesn't clearly set goals and boundaries. Engineers wind up future-proofing because their goalposts are constantly moved. Picture this scenario:

    Engineer: I can build it in different ways. This simple way will accommodate N users, this more complicated way will accommodate M users but will take three times as long. I recommend we do the first option, because it's unlikely we'll see more than N users.  
    Management: no, we want to get to M users, build it the second way  
That sort of situation is exceedingly common. The reality is that the vast majority of engineers don't get to set the product development road map based entirely on engineering concerns.

Re: Stop future-proofing software

#43
post #8

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 requir…

I 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.

Re: Stop future-proofing software

#44

Earlier quoted context omitted.

I admit being confused about this one. Composition or inheritance isn't harder or more work, and neither provides velocity or maintainability benefits. The argument is about how to model complex behavior -- they have tradeoffs in duplication and statefulness. And you can mix both in the same code base with ease. Here's another one though. We need to use Promises despite callbacks being trivially understandable and su…

I think the real takeaway is that composition is much more likely to model the semantics you have, even though inheritance feels like it gives you better code re-use.

I admit to being a composition bigot when green fielding some functionality, and I've never felt like code reuse was a thing either mattered for. I like composition because it's easier to create small testable component contracts. But I maintain plenty of inheritanace based code and, while it's less testable, it's... fine. Not really a big deal to use either or both.

Edit: to be clear, when I hear "code reuse", I think "using a component outside its original intent". Composition does impose some duplication sometimes, but it's braindead plumbing code, not buggy logic code, that gets duplicated. I generally think that cost is worth it to gain more statelessness. Hence my greenfield bigotry

Re: Stop future-proofing software

#45
The way I have approached this problem is to take a look at previous large code bases where I and others inevitably built frameworks and solutions to problems we ran into. They were created because we either couldn’t find a reasonable alternative or our needs were simple that we didn’t need to introduce a lot more complexity to our app. It’s nice to take a step back every once and a while and try and build toy problems where you revisit those problems in architecture and see how you would do it different using today’s existing solutions. I find balance using really simple and boring tech while taking advantage of some frameworks that make life easier (depending on preference)

Re: Stop future-proofing software

#46

This article reminds me of the adage about ops people which goes, "You know you're doing a great job in operations when nobody thinks you do anything." The problem with taking the experience that "future proofing" is never worth it, is that it suffers from survival bias, which is to say that when you did it and it made the future event a non-event, it didn't even register on your brain. If you practice more mindful e…

So, to me, "future proofing" has some very specific connotations. It doesn't mean building for eventualities that you know will happen and already know how to build for (though, if it takes extra time to do so, you do need to be mindful of the cost of delay), and it doesn't mean things like writing well factored and loosely coupled code (within reason) so that you get a system that's generally easier to modify. It me…

Exactly. When people talk about "future proofing," they're imagining the future as a predictable, linear progression--which it definitely isn't. The people best equipped to deal with the complications of the future are those who have direct experience with it ... in the future.

Re: Stop future-proofing software

#47
post #12
post #8

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 requir…

"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...

This 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.

Re: Stop future-proofing software

#48
post #15

FWIW, maybe I'm living in a bubble but I don't seem to run across unnecessary "future proofing" very often. In fact, I see the opposite as a problem much, much more frequently. I work in a boring corporate job though, not a hip startup, so nobody at any of my jobs are trying to be the next Google or Facebook - that's probably the main difference. >We need to hire a team of developers and build in-house software, desp…

I used to see future proofing all the time in boring corporate jobs. One example I saw multiple times was complicated "generic form builders" where the dream was users would be able to add their own forms quickly and easily. Then 5 years later the whole app would be say 10 screens, but maintenance was a nightmare because under the covers everything was poorly abstracted and generic. I also saw lots of "split it into…

I fully admit my experience might not reflect most people's, just sharing mine. Thanks for sharing yours, it's interesting the differences.

Re: Stop future-proofing software

#49

Better idea: dont pretend you are about to be the next google. Worry about that when it happens. I see too many startups burning time/money on scalability they dont need. If you have 1000 customers, and your rack can handle 20000, dont talk to me about future-proofing against the day you are building your own datacenter. Keep your code working for today's customers. Worry about scale when it happens ... IF it happens…

If you're optimizing for investment, I wonder how much of potential funding is tied to your ability to communicate that you have thought the scaling problem out. If your investors are more technical, service oriented architecture and orchestration tools (like k8s) may serve as signifiers that you have some sense of what you're doing.

David Sacks said in his Twitter that's one of his criteria for investment - "What’s the friction to scale? Can the team solve?"

Re: Stop future-proofing software

#50
I just stick to the rule of three: if I see the same pattern three times I consider the abstraction I’m missing.

This surprisingly leads to “future proof” code. I didn’t anticipate the needs of the business. I just wrote what needed writing. Yet over time I build the abstractions in that the code is actually using.

I say surprising because code written this way looks obtuse and... messy. You get very senior and very confused developers asking you why you didn’t apply The Singleton Factory Pattern or use the XYZ architecture. So you say, because I didn’t need it and they just don’t get it.

But that’s part of the reason why I like Haskell so much these days. And why I liked Python for so many years. I like languages that bake in good ideas and abstractions for you. Python baked in the iterator pattern, the decorator pattern, etc so that you could use them in the language with little ceremony. Haskell bakes in the most powerful abstractions of all: type theory and algebra.

And still the idea of only writing the code you need to solve the problem; nothing more or less; stands up. People may look at you like you’ve lost your marbles. But if you can get them to stick around in the codebase for a year or two they’ll start to see it too.

Post reply on HN