4 ms·
Argh. Apparently there are two very popular types of article in the software blog world: Type 1: YAGNI (like this example): Do less now. Refactor later as nee
by telemachos 16y ago
Argh.
Apparently there are two very popular types of article in the software blog world:
Type 1: YAGNI (like this example): Do less now. Refactor later as needed. It won't be needed, most likely. Chill out. (All driven by the question, "Dude, wtf? 100 lines of boilerplate for a 5 line case statement? Snap out of it.")
Type 2: Architect astronautics: Do more now. Build for the next version. You will need more then, so why not prepare now? Decouple that code. Use more patterns. Hoist that jib. (All driven by the question, "What will your code/software/app do if...?")
I read type 1, and it (often) sounds convincing. I read type 2, and it (often) sounds convincing. I get a fucking headache from the cognitive dissonance. I make more coffee and get back to work, no wiser than before.
- edw519 16y agoGreat comment. 2 thoughts: 1. You can read anything and it (often) sounds convincing. Quality writing that is incorrect often trumps poor writing that is correct. That's way we must always be vigilant readers, especially on the internet. 2. "ArchitectingForTheFuture" does not necessarily mean "MoreLinesOfCode". I'd like to read more articles of Type 3: How excellent design and proper use of tools gives you the best of both Type 1 and Type2. [EDIT 1: stcredzero, in reference to cousin comment, my personal metric is n=2. I never want the same line of code more than once. I'm sure there are good arguments for other values of n, but this has always seemed to work well for me.] [EDIT 2: I try to make a point to never say "Not Hacker News" or complain about content. Conversely I should be quick to claim "Not Not Hacker News". This is a great thread! About stuff near and dear to this programmer's heart. Keep 'em coming.]
- stcredzero 16y agoAs I say in a cousin comment: an easy way to get the best of both worlds is to wait until you have an apparent problem. The 4th time you start writing that same switch statement, maybe you can tell you're going to be doing this a lot more and it's time to bring out Strategy. Doing it sooner is too likely to be premature. The advantage of this approach: you never have to prognosticate. Hindsight is 20/20, so use it! EDIT: I used to use n=2 as my threshold, but I found it much better to have a slightly higher n in Smalltalk. I am spoiled, though because I have such lightweight but powerful (syntax-aware) tools for searching such patterns.
- a-priori 16y agoAs always, the best solution is somewhere in the middle of the two extremes. Everything has a cost. YAGNI has a cost of rework later if it turns out you do need it after all. Architecting For The Future has extra up-front and maintenance costs, which is wasteful if it turns out the future wasn't as you saw it. And so, it becomes a cost-benefit analysis. C(YAGNI) = C(develop a simpler version) + C(rework) x p; C(AFTF) = C(develop) + C(maintain). Where p is the anticipated probability that you will actually need that feature (and realizing that you're probably going to guess higher than the actual probability).
- anamax 16y agoThere's a C(rework) term in C(AFTF) as well because the "in advance" work never actually "just works" for the future that shows up.
- jimbokun 16y ago"Everything has a cost. YAGNI has a cost of rework later if it turns out you do need it after all. Architecting For The Future has extra up-front and maintenance costs, which is wasteful if it turns out the future wasn't as you saw it." Isn't that why we write good unit tests and refactor? It gives you close to best of both worlds. Writing unit tests is helpful anyways, and gives you peace of mind when you introduce patterns later on when you actually need them.
- stcredzero 16y agoThey're just two sides of the same coin. YAGNI is the starting point. Be as minimal as possible. Code is just like inventory in a supply chain. Do things as cheaply as possible for as long as you can get away with it. I think you're misinterpreting Type 2. You don't bring out Type 2 to prevent future problems. You bring it out to solve problems. I have a rule: don't apply a pattern or other advanced technique until doing so will eliminate or reduce code in more than 3 places. If the same code starts popping up in more than 3 places, it may show up in a whole lot more, and it's going to start being a pain to find all of the occurrences. On the other hand, just finding 2 or 3 and correcting them is not really that hard, so leaving things until n=4 is not too bad. Going back to the article, never apply a pattern in a language until doing so yields fewer lines of code than the original code. If your language requires N > 10 for this to be true for most patterns, then switch languages! (Someone should turn this into a metric!)
- mechanical_fish 16y agoMy conclusion, as I watch my own code wander back and forth in the space between the two poles, is: One needs a lot of practice!
- jokull 16y ago"practice" or "experience" are the terms missing from this discussion! An experienced person can iterate less and has a better design right off the bat.
- lukev 16y agoI feel your pain - I struggle with the same thing. But I think it's possible, to some extent, to have the best of both worlds. An ideal of clean, logical design actually satisfies both #1 and #2. You don't write any more than you need to, but you architect it sensibly so if you need to modify it in the future, there's a natural way to do it. A case study: you are responsible for maintaining a moderately sized mailing list. Type 1: Maintains a comma+newline separated list of name/email pairs in a text file, parse it with a 3 line perl script that sends an email for each regex match. Done in 15 minutes. YAGN anything else. Type 2: Builds a fully relational model in a database with seperate tables for names and addresses, backed by Hibernate with a complete class hierarchy including AbstractRecords (in case you want to store records other than name/email pairs), RecordFactories (In case we need to generate lists of records from another source), AbstractRecordDAO (in case we need to use a different Database or ORM framework), EmailFactories, AbstractMailerImpl, etc, etc, etc, planning in the architecture for anything anyone might ever want to do with a mailing list. Both of these are wrong, IMO. The correct solution is to write one database table, or one cleanly formatted file, with a program with perhaps two classes that abstract apart the data loading and the mailing tasks. One class loads the data into a simple Map structure, and the other that handles iterating the map and doing the mailing. It only takes a bit longer to write than #1, and is infinitely simpler than #2. It doesn't anticipate every future need, but when one comes, there's a logical point to start adding the functionality. If I have to do something new with the list, I don't have to completely rewrite my program (like #1 does), I just write a new class that uses the existing data structure. Just as extensible as #2, with a tenth of the work. And easy to see what's going on.
- bsaunder 16y agohmm... If I have to do something new with the list, I don't have to completely rewrite my program (like #1 does) You said #1 only took 15 minutes. Hardly seems like a big cost. If you never have to rewrite, #1's the right answer. Or if the rewrite includes functionality never even anticipated in your "correct solution". If it's that small, starting over with a clean slate may be faster than fitting in new functionality.
- lukev 16y ago
- DannoHung 16y agoYeah, this is why some people get paid money to make decisions about software design. Finding a balance between what you ain't gonna need and what you should be prepared for involves experience, a knowledge of the problem space, and just plain smarts.
- zackham 16y agoMy 2 cents: Go for (1), keep several potential future use cases in mind, and make damn sure you don't architect yourself out of those being natural directions for the code to go. Often this leads you to stub out an interface that looks like the beginning of a nice architecture, but not actually flesh out the code beyond what is necessary. This strategy generally requires more thinking than coding, but in my experience that is usually a good thing.
- bmm6o 16y agoIt's worse than that. The whole article is attacking a straw man, which so many software blog articles do. I'm not sure I would take the example in a Wikipedia article to be the pinnacle of software engineering. Sure, it would be strange to see the sample code appear as-is in a code base. I would agree that a strategy for adding and subtracting might be overkill. But it's an example! It's purpose is to convey an idea. If you're going to critique it, you really have to do so on those grounds.
- radu_floricica 16y agoSometimes the world just is as simple as it seems. Type 1 is right, type 2 isn't. After 15 years of writing code I can only vouch for two metrics of code quality: clarity and length. For a long time I too thought that clarity means using "thisIsAStudentBirthDate" variable names - but it turns out there are very very few instances where the shortest code isn't also the most obvious at first glance. I think the turning point was when I decided rewrites aren't a bad thing. I started seeing the code as alive - unchanged code isn't good, is dead. The myth of perfect code is just an illusion. Writing for the next version sounds very sensible - until you get enough years under your belt to realize that either there won't be a next version or it won't be anything like you expect.
- j_baker 16y agoType 1 is right for you. A good type 2 knows they can't anticipate the future and prepares for the unknown. If overdone, this is bad, but the same can be said for type 1. And rewrites do become difficult as a system grows if you don't think them through. It's easy to rewrite one component with no dependencies, but it's not so easy to rewrite something that lots of different pieces of code rely on.
- radu_floricica 16y agoA problem with a verbose approach is that when ver 2.0 comes you can't really know/remember which part of the code is actually used and which was just prepared for future development - so you end up maintaining and coding around pieces of code that will never be used. Like I said, what eventually bites you is that you never ever ever know exactly what version 2.0 will require. So in the end you still have to adapt code, and adapting clean short code is easier.
- j_baker 16y agoIf you have to explicitly remember which code was prepared for future development, you're doing something wrong. The point of having loosely coupled code is that it should be easily modifiable under a wide variety of circumstances.
- j_baker 16y agoThe reason is that neither view is correct or incorrect. They're just different. Reality is in the happy medium: preparing for the future without overengineering.
- Tamerlin 16y agoExperience WILL tell you that the best answer is ALWAYS #1. Don't make it complicated unless you absolutely have to. 99% of the time, you don't, and the architects are full of it, but if they ever admit that, then they lose their job security. When in doubt, remember what Knuth said: "Premature optimization is the root of all evil."
- ecaradec 16y agoType 1 design is not taught anywhere, people don't even know this is an option. The only design strategy that is taught is architecture astronautic. I had an argument with a teacher that thought that it would be useful to abstract things from a pop/imap email fetching service, in case other protocols show up ? I'd argue that was improbable and that would complexity the design and that possible other protocols might be so different that they wouldn't fit the abstraction. He dismiss it as industry specific way of doing (but I lacked examples a little bit, so I wasn't completely convincing ). We have to be quite vocal because we cry against the mountain.