3 ms·
> There is nothing wrong with this function. Really. It does exactly what it should do with the least amount of code. In fact, this is probably what I want to d
by MaxBarraclough 6y ago
> There is nothing wrong with this function. Really. It does exactly what it should do with the least amount of code. In fact, this is probably what I want to deploy in production as well. But, it is inflexible.
It does one well-defined thing, and does it well! That's the good kind of 'inflexible'.
> A skillful programmer looks at the problem and says, hey, why don't we build an abstraction layer to make it more flexible?
In this example, the additional complexity just doesn't seem justified. You ain't gonna need it. [0] Unnecessary abstractions cause bugs, bloat, and baggage. The article then goes on to acknowledge this, but I really question the chosen example.
> Just having these few abstractions opens up a lot of possibilities. Why? Let’s suppose that you have 3 different kinds of fetchers and 3 kinds of notifiers. Now, you can easily compose 3 x 3 = 9 different kinds of health checks.
You've just introduced an exponential explosion into your testing burden. The pay-off: additional functionality that might be useful later.
edit On reflection I suppose it's not a 'exponential explosion', it's merely a 'multiplicative explosion'.
> Enjoy your programming life. It's a fun art!
The article doesn't go into detail on what it means here, but the more serious the software work, the less the artistic mindset applies. [1]
I think the most sensible approach to software engineering is to draw satisfaction from solving a challenging problem well. Things like 'programmer freedom' strike me as false idols.
That said, I'm a sucker for elegance (a famously subjective commodity), so maybe I'm a hypocrite.
[0] https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it
[1] https://www.fastcompany.com/28121/they-write-right-stuff https://www.fastcompany.com/28121/they-write-right-stuff
- horsawlarway 6y agoDing ding ding. I agree one hundred percent. This isn't elegant, this is needlessly over engineered and now much more likely to slow development. The biggest cost isn't lines of code, it's understanding abstractions. What happens when an "abstractnotifier" blows up? Where does the error happen? what are the possible side effects? What systems might be impacted? What happens when 'alert's start failing? Is it queued/buffered? Do I need to worry about OOM errors on systems? Will it retry? I lead a team of developers - This kind of abstraction tends to slow everything down. The only place it fits is during a refactor of software delivering real business value already. Basically - You can do this if you know exactly what your requirements are, and this nicely solves them. If you do this before you understand EXACTLY what you need, you're going to be wrong. You will abstract the wrong pieces, with the wrong assumptions. I promise you. Then later you have to rip it all apart anyways, except now instead of 4 simple lines with clear consequences, you have a bloated monster of bad assumptions.
- deleted 6y ago[deleted]
- goostavos 6y ago>If you do this before you understand EXACTLY what you need, you're going to be wrong. The main problem with this is culture imo. I agree 100%, but it is so hard to fight against the idea that "real engineering" or being a "good developer" means tiers of abstraction. To me, being a good developer actually means understanding what your operating parameters are, and then writing the smallest amount of code that satisfies the business requirement. Sometimes (a lot of times!) this DOES mean you have to go back and refactor or completely throw away code. By _design_ you chose to only solve the immediate problem. However, culturally, this is seen as a failure rather than a success.
- tomkwong 6y agoOP here. The code presented in the blog post was just an example although your points above are well taken. Practically, the decision to do abstraction or not depends on the use case and requirements.
- tomkwong 6y ago>You've just introduced an exponential explosion into your testing burden. The pay-off: additional functionality that might be useful later. > edit On reflection I suppose it's not a 'exponential explosion', it's merely a 'multiplicative explosion'. This statement seems rather inaccurate and misleading. What you thought about 'multiplicative explosion' is the number of integration tests required. Generally, software testing focus more on unit tests, and so in this case, the number of tests is still linear. > The article doesn't go into detail on what it means here, but the more serious the software work, the less the artistic mindset applies. [1] This claim seems to be too opinionated. I doubt seriousness makes a difference. I have seen artistic/creative design in some very serious projects before.
- MaxBarraclough 6y ago> What you thought about 'multiplicative explosion' is the number of integration tests required. Generally, software testing focus more on unit tests, and so in this case, the number of tests is still linear. I don't follow. The new function has 9 different modes of operation to be tested. I don't see the relevance of the distinction between unit tests and integration tests. The alternative is to decline to test them all, of course, risking a trade-off between bugs and baggage. Even in this trivial example, there's a bug resulting from inadequate testing. [0] Perhaps in this example we might be confident enough in the independence of the two parameters that we wouldn't bother testing all 9 combinations, but my general point stands. > I have seen artistic/creative design in some very serious projects before. What kinds of projects? Avionics? Formally verified systems? Formal development solutions like SPARK, and informal frameworks like MISRA C and other restrictive coding standards like [1], greatly restrict the programmer's freedom. [0] https://news.ycombinator.com/item?id=23365655 https://news.ycombinator.com/item?id=23365655 [1] https://news.ycombinator.com/item?id=22192656 https://news.ycombinator.com/item?id=22192656
- xg15 6y ago> I don't follow. The new function has 9 different modes of operation to be tested. I don't see the relevance of the distinction between unit tests and integration tests. The alternative is to decline to test them all, of course, risking a trade-off between bugs and baggage. Even in this trivial example, there's a bug resulting from inadequate testing. [0] Perhaps in this example we might be confident enough in the independence of the two parameters that we wouldn't bother testing all 9 combinations, but my general point stands. I think what he means is that, in unit testing, you only test code units in isolation to each other, with the tests assuming the other parts that your unit-under-test interacts with behave exactly as the unit-under-test expect: If you had n producers that could plug into one of m consumers, you'd test each producer in isolation against a mocked consumer and each consumer in isolation against a mocked producer. That will give you n + m unit tests, which is indeed linear. Of course this hides the fact that in practice, you'll have n * m different modes of operation, so you'd need n * m different integration tests if you actually wanted to test each possible interaction. You can get away with only testing units in isolation if the interface/API/protocol between producers and consumers (in this example) is well-defined and rigid enough that implementation details in one part really have no effects on the other part. I think there is lively debate how well this is possible in practice - in effect, this is the whole point of standardized interfaces. However, even attempting this is often a lot harder than it appears and therefore shouldn't be done if not necessary. That being said, I think it's important to keep in mind that you can almost never write tests for all possible configurations that a program could run in, if your program is non-trivial. You get combinatorial explosion pretty fast, even if you don't use any abstraction at all. And after all, the whole point of a program is that you can feed it with novel inputs without knowing what the exact output will be - becasue finding out that output is what you wrote the program for. So you'll always have to think about classes of inputs and restrict your tests to (hopefully representative) examples of each class.