The "fallacy" breaks down pretty quickly when we consider its implications. If we were to follow the suggestions given, we shouldn't be using:
- Email services such as SES or Sendgrid
- Password auth services such as Firebase
- Chat services such as Twilio
You might say those are extreme examples, and that's the whole point. Clearly there are a ton of use-cases where it makes sense to build reusable services once, and using it across many clients. Not just for "enhancements", but as an integral part of your service's functionality. So how and where do we draw the line between reusable services and avoiding service-dependencies? That's the real question we should be asking, and one I don't see being answered in the article.
This is what I was wondering. The entire concept of AWS is built around "re-use". Forgive me if I'm wrong but didn't Bezos decide to rebuild amazon because of technical debt and wanted to build independent "re-usable" components. The entire thing was modeled as though teams were really companies. Each "team/company" (think of RDS as a team) defined their API to the overall guidelines, and then other teams interacted with them to make use of their service. He realized the business case for this and AWS was born. Maybe that's just some fake story I read somewhere.
Anyway the entire concept of B2B also challenges this. We don't build a new company each time we need something new. Even assembly language doesn't agree with this.
I look at it like this. It’s super useful to have a screwdriver that has an adaptor that will take many different kinds of bits. It would be super painful to have a really configurable bit that could with some work fit any screw.
My exemplar is a library for doing things with grids. It supported any kind of 2d grid. It was an amazing piece of design from a conceptual standpoint. Super generic, clean and with some impressive demos doing very non-standard things. It was also much easier for me to write my own grid implementation for my usecase than learn how to use.
Being reusable because you do something well is great. Being “reusable” because you can be made to do one thing out of a bunch once you learn how is not. From a business point of view it’s about how valuable that flexibility is and generally I think if it’s not part of the core business you are better finding an inflexible fit rather than a flexible thing you have to sculpt to work. Simply because the latter takes focus away from what you are actually trying to do.
The super complex generic grid library is also probably useful for someone but I’d say neither case is more universally useful due to the complexities involved.
Interesting because in those examples the need for reuse was so high that not only is the code reused but an entire company is set up, paying staff including sales and marketing people not working on code itself but making people aware of the solution. In fact there are multiple companies providing each of those services.
With internal code, there might be a team per micro service or maybe 0.25 of a developers time devoted to it.
Not saying for or against microservices but they can’t be compared to SaaS services
>The latest paradigm “Microservices” became popular in the mid 2010s.
Microservices isn't for re-usability. It's main benefit is better encapsulation, which should let you do independent resource management/deployments/failures.
>If we look at reusability, we notice that this is the tightest form of coupling possible: If the reused part is not available, the reusing part cannot complete its work by definition.
What? This is a weird nothing-burger of a sentence. Why can't multiple components re-use some resource without gracefully handling any failures? Why does re-usability mean "cascading failures by design"?
>An important property of reuse is that the reusing part ... does not work without the reused asset ... The reused functionality is an integral part of the solution that (re-)uses it.
Oh, I see: because the author has defined it this way. Then the article goes on to try to argue a semantic difference between "usability" and "re-usability", in that usable components can be re-used independently, but in a way that can gracefully fail! So the whole thing is just begging the question.
There's no argument here as to why I can't write a distributed component with an eye to it being re-used in the future. There is no criticism of re-use in distributed systems. The only criticism is of tight coupling, which is totally valid, and then saying that re-use demands tight coupling, which is not substantiated.
>An important property of reuse is that the reusing part ... does not work without the reused asset
Its also naive to expect that implementing your own local provider (if even an option for you) will generally improve on the reliability over a remote/reused provider.
Isn't that AWS Lamdba environment getting reused at a very high frequency?
Maybe what we should consider is that coarse-reuseabulty--thinking OS and container images, cloud services--works semi-well, but that the more fine the grain of the re-use at the application level, the more TFA author's points kick in.
I think the concept the author is talking around here can be stated more simply:
When you refactor software to reduce duplication, you by default are increasing coupling.
At the start, you some lines of code that were just sitting in the bodies of two different functions. Now, both of those functions call one shared function. But, in a proper minimal-change refactoring, you didn't change the semantics of the code by doing this. So the shared function is still acting the way that its parents did: as sets of lines sitting in the bodies of functions. There's no circuit breaking or graceful failure there, because there was no circuit breaking or graceful failure in the original, non-refactored code.
Another way to put this is: compression makes data more fragile, in the sense that small numbers of bits can be "depended on" by more bits in compressed data than in uncompressed data. Direct duplication-elimination refactoring is, essentially, just compression.
To actually get robustness, you can't just do this kind of refactoring and then stop; you have to continue on and introduce a stable API to your factored-out function/component/service, with a specified interface. Once you do that, then you cancel out the increase of coupling, because now everything is just coupled to a description of the interface. Then you get the ability to re-use the function/component/service. Until you do that, though, you just have something that is known to work exactly in the use-cases it was extracted from; and which shouldn't be expected to work anywhere else.
I'd agree with you for this post, minus the weird re/usability semantics, but that interpretation goes against the previous parts of this series.
If you read Part 1, the argument is largely against even well-architectured versions of re-usability. The author takes issue with all components designed for drop-in future use, believing that each problem requires a tailor-made solution.
The re/usability debate seems weirdly entangled with the coupling debate for no good reason. The author believes re-usability means tight coupling, and "usability" means good APIs with loose coupling (which can be re-used...).
From my experience actual good reusable code are very small and usually relatively simple great specialized task. As the component get more features, and when you use it, you are in a trade off situation where this works, but it doesn’t fully do exactly what the requirement needs orbit does way more than it needs
I think about reuse in a couple of different ways.
I do not copy paste the same exact code multiple places. Then i use the same code.
If the code is not exactly the same, it's not a copy, hence it's not up reuse.
I do not generalize code before 3rd strike and refactor can be applied, and then I still use a lot of time thinking if it might a premature optimization.
I write microservices the same way as i write methods. If i copy and paste the exact same microservice, it might be up for reuse (3 strikes). If it's reuse, the same specifics applies as when programming a method or type.
I would never ever write any new code with reuse in mind. It's arrogant to think one can forsee the future, and that's exactly what writing for rejse is about. Start small, evolve.
Java has the opposite problem. One of the often repeated taglines is "If you're unhappy with X, you can easily replace it with Y, Z, or Q!"
I just want one well-working thing, not twenty alternatives - seven of which turn out to be abandoned, three incomplete, one only works with Java 15 beta, and two are actually forks of the same abandoned pre-Oracle blogpost from Sun.
Like my own his English is good but not that good. His seems the right way to explain (there probably is a formal description for the style of writing (and a word for it in German)) but when reception is not guaranteed (I keep feeling like I almost got his point.) I would lower myself to explaining by code example. Something like a "hello world" of usable vs re-usable. Someone more skilled in the art should probably address note 6 ( https://www.ufried.com/blog/reusability_fallacy_3/#fn:6 )
I did recall a thought I had some years ago that I will describe crudely because I'm lazy:
Parts of applications, by popularity, should be re-implemented in API's.
Parts of API's, by popularity, should be implemented in modules.
Parts of modules, by popularity, should be implemented in frameworks.
Parts of frameworks, by popularity, should be implemented in (higher) languages (then lower)
Parts of languages, by popularity, should be baked into hardware.
(Unpopular parts should migrate the other way around but that is not important right now)
What do we mean by "software reuse"? I propose this definition: Software reuse happens whenever a given piece of code is called from more than one other code-location.
That means whenever a function has more than one caller we have reuse. Or when a web-service request is created from more than one code-location, we have reuse.
Is there an official definition for what "software reuse" means?
21 comments
[ 3.0 ms ] story [ 53.4 ms ] threadPart 2: https://www.ufried.com/blog/reusability_fallacy_2/
Part 1 was posted previously but without much discussion: https://news.ycombinator.com/item?id=22583605
- Email services such as SES or Sendgrid
- Password auth services such as Firebase
- Chat services such as Twilio
You might say those are extreme examples, and that's the whole point. Clearly there are a ton of use-cases where it makes sense to build reusable services once, and using it across many clients. Not just for "enhancements", but as an integral part of your service's functionality. So how and where do we draw the line between reusable services and avoiding service-dependencies? That's the real question we should be asking, and one I don't see being answered in the article.
Anyway the entire concept of B2B also challenges this. We don't build a new company each time we need something new. Even assembly language doesn't agree with this.
They did the same thing for logistics too, building a service for each logistical task they do (warehousing and delivery)
My exemplar is a library for doing things with grids. It supported any kind of 2d grid. It was an amazing piece of design from a conceptual standpoint. Super generic, clean and with some impressive demos doing very non-standard things. It was also much easier for me to write my own grid implementation for my usecase than learn how to use.
Being reusable because you do something well is great. Being “reusable” because you can be made to do one thing out of a bunch once you learn how is not. From a business point of view it’s about how valuable that flexibility is and generally I think if it’s not part of the core business you are better finding an inflexible fit rather than a flexible thing you have to sculpt to work. Simply because the latter takes focus away from what you are actually trying to do.
Why would it? Universal sockets exist: https://images-na.ssl-images-amazon.com/images/I/61UczrhrTuL... - I can imagine one built with small enough pins to push into/around any kind of screw head, although I don't know if one exists today.
With internal code, there might be a team per micro service or maybe 0.25 of a developers time devoted to it.
Not saying for or against microservices but they can’t be compared to SaaS services
https://books.google.ie/books?id=U896iwmtiagC&pg=PA45&lpg=PA...
Microservices isn't for re-usability. It's main benefit is better encapsulation, which should let you do independent resource management/deployments/failures.
>If we look at reusability, we notice that this is the tightest form of coupling possible: If the reused part is not available, the reusing part cannot complete its work by definition.
What? This is a weird nothing-burger of a sentence. Why can't multiple components re-use some resource without gracefully handling any failures? Why does re-usability mean "cascading failures by design"?
>An important property of reuse is that the reusing part ... does not work without the reused asset ... The reused functionality is an integral part of the solution that (re-)uses it.
Oh, I see: because the author has defined it this way. Then the article goes on to try to argue a semantic difference between "usability" and "re-usability", in that usable components can be re-used independently, but in a way that can gracefully fail! So the whole thing is just begging the question.
There's no argument here as to why I can't write a distributed component with an eye to it being re-used in the future. There is no criticism of re-use in distributed systems. The only criticism is of tight coupling, which is totally valid, and then saying that re-use demands tight coupling, which is not substantiated.
Its also naive to expect that implementing your own local provider (if even an option for you) will generally improve on the reliability over a remote/reused provider.
Isn't that AWS Lamdba environment getting reused at a very high frequency?
Maybe what we should consider is that coarse-reuseabulty--thinking OS and container images, cloud services--works semi-well, but that the more fine the grain of the re-use at the application level, the more TFA author's points kick in.
When you refactor software to reduce duplication, you by default are increasing coupling.
At the start, you some lines of code that were just sitting in the bodies of two different functions. Now, both of those functions call one shared function. But, in a proper minimal-change refactoring, you didn't change the semantics of the code by doing this. So the shared function is still acting the way that its parents did: as sets of lines sitting in the bodies of functions. There's no circuit breaking or graceful failure there, because there was no circuit breaking or graceful failure in the original, non-refactored code.
Another way to put this is: compression makes data more fragile, in the sense that small numbers of bits can be "depended on" by more bits in compressed data than in uncompressed data. Direct duplication-elimination refactoring is, essentially, just compression.
To actually get robustness, you can't just do this kind of refactoring and then stop; you have to continue on and introduce a stable API to your factored-out function/component/service, with a specified interface. Once you do that, then you cancel out the increase of coupling, because now everything is just coupled to a description of the interface. Then you get the ability to re-use the function/component/service. Until you do that, though, you just have something that is known to work exactly in the use-cases it was extracted from; and which shouldn't be expected to work anywhere else.
If you read Part 1, the argument is largely against even well-architectured versions of re-usability. The author takes issue with all components designed for drop-in future use, believing that each problem requires a tailor-made solution.
The re/usability debate seems weirdly entangled with the coupling debate for no good reason. The author believes re-usability means tight coupling, and "usability" means good APIs with loose coupling (which can be re-used...).
I do not copy paste the same exact code multiple places. Then i use the same code.
If the code is not exactly the same, it's not a copy, hence it's not up reuse.
I do not generalize code before 3rd strike and refactor can be applied, and then I still use a lot of time thinking if it might a premature optimization.
I write microservices the same way as i write methods. If i copy and paste the exact same microservice, it might be up for reuse (3 strikes). If it's reuse, the same specifics applies as when programming a method or type.
I would never ever write any new code with reuse in mind. It's arrogant to think one can forsee the future, and that's exactly what writing for rejse is about. Start small, evolve.
I just want one well-working thing, not twenty alternatives - seven of which turn out to be abandoned, three incomplete, one only works with Java 15 beta, and two are actually forks of the same abandoned pre-Oracle blogpost from Sun.
I did recall a thought I had some years ago that I will describe crudely because I'm lazy:
Parts of applications, by popularity, should be re-implemented in API's.
Parts of API's, by popularity, should be implemented in modules.
Parts of modules, by popularity, should be implemented in frameworks.
Parts of frameworks, by popularity, should be implemented in (higher) languages (then lower)
Parts of languages, by popularity, should be baked into hardware.
(Unpopular parts should migrate the other way around but that is not important right now)
That means whenever a function has more than one caller we have reuse. Or when a web-service request is created from more than one code-location, we have reuse.
Is there an official definition for what "software reuse" means?