11 ms·
Minimalism: Practical Guide to Writing Less Code (2002) [pdf]
- mingodad 8y agoLooking around int the web http://www.jbox.dk/ http://www.jbox.dk/ refered on this submission https://news.ycombinator.com/item?id=18829779 https://news.ycombinator.com/item?id=18829779 about Sanos operating system I found this pearl.
- Dowwie 8y ago"Recommendation 2: Include Only What You Need" The slide refers to the inversion of control principle but can easily include other important aspects of including only what is needed. I would like to expand on this for discussion. Consider the challenges of not over-engineering solutions to problems you and others don't fully understand, yet. Unless you work for NASA, you won't fully know what you need until you need it. How do programmers here account for the unexpected?
- AlexanderDhoore 8y agoYou don't because you can't. It is impossible to predict the future so please stop trying. Just implement what you need today and do the rest later. "Always implement things when you actually need them, never when you just foresee that you need them" https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it EDIT: The way I think about this is that software development has a very large degree of unknown unknowns. More than maybe other domains like architecture/chemistry/...
- TotempaaltJ 8y ago> Just implement what you need today and do the rest later. I'd add that, while you shouldn't change the complexity of your implementation very much, you should look at potential future scenarios and migration paths. Sometimes you can make small changes to the plan that make migration paths easier. Optimize for ease of change I suppose. Obviously the effort you put into this should be proportional to the complexity/importance of the problem.
- koolba 8y ago> Sometimes you can make small changes to the plan that make migration paths easier. Optimize for ease of change I suppose. This works fine for known future changes, say if you’re implementing X, and know that a similar Y and Z are down the road. Beyond that it leads to the usual problems of crystal ball based planning.
- vincnetas 8y agoAnd here comes the part where experience plays its role. History tends to repeat itself, so if you been/seen certain situations you can account for high possibility of them happening again.
- menzoic 8y agoHow does NASA fully know what they need? I would think they have the most unknowns out of any other engineering organization.
- marcosdumay 8y agoThey mostly don't get to take their equipment back to update it. So they better know very well what they need. (Yes, there's some amount of remote update, they still can't move fast and break things.) As usual, in engineering, needs create capabilities. It is very expensive and risky to go fully determining your needs before execution, but they pay for it.
- eps 8y agoListen to the user feedback and _selectively_ act on that. Not all feedback is good, not all of it is applicable, requests for marginal features that benefit only few are exceedingly common. All feedback should be taken strictly in advisory capacity and assessed critically. However if you did miss something useful or dearly needed, it will surface almost immediately. We've been practicing this for ages and it works extremely well.
- scarygliders 8y ago> How do programmers here account for the unexpected? You don't. At least, I don't. When I'm writing code for an application, I'm doing the following... "Hmmm, I need a function to perform <certain thing>" Function is written... tested... debugged until <certain thing> is achieved. Job done. Whilst writing said code, I do not think to myself "Hmmm... y'know in the future it might need to also do <some future thing>" and proceed to add additional function parameters and write additional code which would only be useful sometime in the future, just in case. No. I write sufficient code for what is required of said function at that moment in time. It is only if and when required would I then add any additional parameters and code to that function. Or write new functions.
- klausjensen 8y agoWhen I started as a developer 20 years ago, this was the most difficult thing for me. I would tend to over-think and include WAY too many "what if"-scenarios. So that is what I try to teach junior developers today: Only write what is required now and don't think too much about what the function might be expanded to do in the future.
- pytester 8y ago>"Hmmm... y'know in the future it might need to also do <some future thing>" Those are my trigger words. My experience is that about 97% of the time somebody says those words (in the past it's been me) in the context of writing software and it turns out that: * <some future thing> is never required. * <some future thing> is required but it ends up being needed in a completely different form, rendering the preparation work pointless. * <some future thing> was nice to have but it ended up so far down the list of priorities that it might as well not have been. This applies to every level of the stack from one off functions to grand features, to refactoring, testing and even to things that many people consider "best practice". I don't think it pays to be an extremist about many things in software but "if there isn't a glaring need for it then don't implement it" is one of them.
- javajosh 8y agoThere's a difference between not adding bells and whistles you might not need, and shipping asymmetric, broken abstractions. Just because your app doesn't need to support vectors in the negative plane, for example doesn't mean you should ship a vector library that doesn't support negative numbers. In the same way I see well-meaning devs "dribble" data into an API, forcing future users to expand the API into a shape that could easily have been anticipated, and made cohesive sense. This stinginess with API is particularly bad because now developers learn to not respect API boundaries at all, because there aren't any. (And additionally it contributes to a lot of unnecessary indirection and typing because you have to define subset types at both ends of the API.) So, yeah, DON'T just add arbitrary stuff because you'll think it will be useful to someone; but DO add stuff to maintain the symmetry and understandable abstraction of what you've made.
- nurettin 8y ago>> How do programmers here account for the unexpected? I work on systems that need to run reliably. However, they depend on data collectors (like sensor data or web scraping) that are not known for their reliability or scalability. We do lots of retries, log errors and failures, and patch problems until things work as expected. One thing that helps is the use of sentry.io . It alerts us about exceptions and detects regressions/regression fixes by checking if there were any commits that might have caused or fixed issues.
- entity345 8y ago> How do programmers here account for the unexpected? This is where good architecture and design come into play. Design for what you know and need now but use good principles (OO, etc.) so that code can be modified in a sane way.
- ummonk 8y agoImplement what is needed now while making sure that your code is simple and easy to modify, extend, and refactor.
- karmakaze 8y agoI think most experienced devs here are talking about one side or the other of balancing benefits/costs. Yes, there are pros/cons of taking it further one way or the other. The best way I've been able to prioritize is to the the current code primarily serve current needs. If there are other likely and soon expected capability, make some accomodations that don't negatively impact the complexity of serving the current function. If they're at odds, merely document the ideas to handle the expanded case. The situation to avoid is to pay a complexity tech debt now, for something that may not occur for some time, possibly never with all the maintenance of current features upon that complexity. A common reoccurrence is generalized methods. There was a PATCH endpoint for main entity which I'm sure started out innocently enough with maybe just two change sets. Following the intents through the layers of this choke point implementation is unpleasant. We could achieve the same by composing variants at a higher level so that each concern is separated from the others, but then we introduce this 'machinery' that may not be warranted. If there are many or a dynamic components then it may be the best solution but not until. I recently fell for this myself. On recognizing that a particular service was just a combination of two state machines, I abstracted the state machine operation and applied it to each subproblem. It worked like I expected but the result wasn't right. The state machine machinery was more visible than the core logic. I eliminated the concrete abstraction and created two implicit state machines that invisibly did what was required for each subproblem. If we already had a common state machine form that we were familiar with, things could be different but not when introducing it into a codebase for the first time.
- growtofill 8y ago(2002) Also, previous discussion: https://news.ycombinator.com/item?id=5024221 https://news.ycombinator.com/item?id=5024221
- hibbelig 8y agoI struggle to understand this; I guess the speaker added a lot of information verbally.
- yura 8y agoIndeed. Apparently the author didn't think that minimalism included simplicity of language ("Baroquecratic", really?) and not adding tangentially related and unnecessary quotations in every page.
- Kaveren 8y agoI vehemently disagree with the idea that returning from a function often is bad and that it should be used "sparingly". I find that code that returns as early as possible is the best. The idea of a nearly mandatory single return point is a very antiquated notion. I'm much more concerned when I see ten levels of indentation for no good reason. Likewise with continue and break. I think this advice is very bad. All the other recommendations seemed fine.
- kieckerjan 8y agoSecond that. I disagree with the premise that such code is hard to understand. On the contrary: when done well, early returns pop your mental stack and free up mental resources when reading code. (I guess I am a bit extreme. I even advocate judicious use of goto's in C code, for instance for cleaning up state after an error or for writing out FSM's without the distracting fluff of loops and switches.)
- hliyan 8y agoSeconded. http://wiki.c2.com/?GuardClause http://wiki.c2.com/?GuardClause
- Someone1234 8y agoReturning multiple times can also reduce nesting, since you can add guard-like statements and return early. Take this contrived example: function foo(bar) { var result, error; if(bar is null) { error = "{NameOf(bar)} is required"; result = null; } if(bar is not null) { ... Main function logic ... } return Tuple(result, error); } If you can return multiple times instead you'd be able to write: function foo(bar) { var result, error; if(bar is null) { return Tuple(null, "{NameOf(bar)} is required"); } ... Main function logic ... return Tuple(result, error); } Assume "Main function logic" has several of nest of its own (branching, loops, etc) it quickly gets difficult to read.
- deleted 8y ago
- mihau 8y agoIs there some version of this pdf with code examples ?
- majewsky 8y agoWhat an ironic question.
- mihau 8y agoSome recommendations in this pdf are sooo vague and lack proper context. For example, what the hell does this mean: "Let the Code Make the Decisions: There is no need to spell everything out in minute detail".
- AlexCoventry 8y agoMajewsky was mocking the document, not you, I think. It's maybe taken minimalism a little too far.
- majewsky 8y agoI was not really mocking anyone. Just noting the irony of someone asking for more code in a document that is about writing less code.
- christophilus 8y ago> Prefer code to comments I used to make this same argument, and then took it overboard by never commenting. After reading some literate codebases, I’ve changed my mind. Code is (almost?) always obvious to you as you are writing it. So, you never recognize code that is in need of a comment until you come back to it and struggle to understand its purpose and the context that gave it birth. I now follow a rule: a meaningful comment at the top of each file. A comment on every exported function / value. Often, in the process of writing the comment, I realize I’ve poorly named something or that I’ve failed to handle some case. Comments help guide the reader, but also the writer.
- convery 8y agoI usually just consider my opensource commits to be 'learning opportunities' for others; so I comment on every other line. It's actually pretty nice as a way of 'inlined duck debugging' that helps catch flawed reasoning, even when the code is 'obvious'. Example: https://github.com/Convery/Desktop_cpp/blob/VersionDD/Source/Appmain.cpp https://github.com/Convery/Desktop_cpp/blob/VersionDD/Source...
- bwilliams 8y agoI think that’s fine as a choice for a personal project, especially since your committed to it. It’s a totally different story when working with a team though. Comments become outdated, files get larger, and it becomes more of a hassle to maintain while providing little to no value most of the time.
- christophilus 8y agoComments becoming stale is a real problem. But it is one that is solved by code review and culture, not by fewer comments. In a team setting, comments like these are even more important, as it gives context and higher level meaning to the code without forcing you to jump all over the place through small functions (which would be an alternative way to make this self documenting.
- christophilus 8y ago> Consider duplicate code to be a mistake I partially disagree. Some of the worst code I’ve seen was an attempt to reduce duplication. It’s easy, especially in UI code, to mistakenly identify duplicate code, and prematurely build a DRY “solution”. Premature DRY is the root of much evil. I try to follow the rule of three before trying to identify duplication. Even then, I’ve become much more cautious than in my younger days.
- preommr 8y agoIt's hard because you often don't know what you don't need before you don't need it. Duplication for < n is good in principle until you realize some coworker decided to make their own system or went way past n with end result being it'll take more effort to refactor than to have initially designed something robust.
- gmueckl 8y agoIt really depends on how complex the thing is that you have n copies of in your codebase. Also, is it a concrete algorithm? Is it a pattern? A concrete algorithm can usually be factored out more easily than a pattern, because the later needs to be flexible and extensible. Supporting all the variations of a pattern in a complex codebase in a single place can be challenging and in the end it may be harder to get right than maintaining the repetitive code.
- miclill 8y agoBy "rule of three" you mean if code exists three or more times you deduplicate? Because wikipedia says this about rule of three: [https://en.wikipedia.org/wiki/Rule_of_three_(writing) https://en.wikipedia.org/wiki/Rule_of_three_(writing)]
- TravelAndFood 8y agoYes, like if you see the same few lines of code in two different places, you leave it, but in three different places, you make a separate function.
- 8y ago
- taeric 8y agoMain rule for writing less code: only write the code you need to solve the actual problem you have. It is fine to write towards future problems, but all to often trying to be generic explodes the codebase and leads to bugs.
- 1penny42cents 8y agoCould someone explain these recommendations more in-depth? (5) Manage Resources Symmetrically (10) Let the Code Make the Decisions What does it look like when you do the opposite of these rules? I think I'm unfamiliar with the problems these slides address, so I can't grasp the insight in them.
- brachi 8y agoFor many real world examples of minimalism and simplicity, see projects such as st or dwm from suckless. http://suckless.org/philosophy/ http://suckless.org/philosophy/
- s4vi0r 8y agoSuckless is at best satire and if we're being honest just plain idiotic. Without fail every suckless person I've talked to either has no idea what they're talking about, or in the off chance they do they're incredibly shitty far right wing types who gripe on about women/minorities/CoCs ruining tech.
- thanatropism 8y agoThat's vague. Please tell us more.
- mhd 8y agoYikes, that sounds like a horrible experience. Never got that from hanging around at the Arch linux forum back in the days where their products were moderately popular. Having said that, my general impression was that there was an inordinate amount of Plan9 cargo-culting (with some DJB fanboys mixed in). Young, idealistic people without much experience but lots of admiration for the more Bauhaus part of their elders.
- jeffdavis 8y agoWithout understanding why these rules get broken, it's hard to avoid breaking them. There is a tension between dependency and redundancy. Avoiding redundancy often introduces dependency as code becomes shared for several purposes. Knowing which is preferable (dependency or redundancy) in a given case requires a lot of judgement and changes as the code evolves. Redundancy is bad when it introduces too much code and too many chances for error or unnecessary divergence of implementation. Dependency is bad when two things that start out closely related diverge in purpose, straining an implementation to handle both cases.