What do you see in the picture? A piece of cardboard? Some junk? No! — It’s a model!
Thread on models and bounded contexts 1/9
#DDDesign#BoundedContext
It's interesting how people insist on writing tests after the implementation to lock down the implementation, and then complain that writing tests locks down the implementation, making it hard to change.
I discussed this in the book Clean Architecture. Business rules are divided into two species. Entities are pure business rules that don’t know what application they are executing in. Use cases are application specific business rules. Transactions are application specific and are mediated by the use cases. The entities, know nothing about transactions.
A user story (the user's story) provides value _only_ if it makes the user's work go better in the domain. Period. That doesn't mean you don't need to do overhead work occasionally, but that is _not_ value-add work, and if you spend too much time on it, you'll start losing customers. Delivering value is entirely about our customers getting _their_ work done. It is entirely about giving customers a reason to buy our product. Nobody will buy our product because the database is organized better. They buy our product because it helps them get _their_ work done. It's about making their lives easier. That's why Agile focuses on customer value: to assure that we're building something somebody will buy. So, vis-a-vis stories, if nobody will ever log in to the system to do whatever work the story describes, then that thing (I hesitate to call it a _user_ story) has no customer value. It's overhead. And as I said earlier, overhead work may be necessary, but it does NOT add customer value, so it needs to be managed carefully.
Also, customer value is _direct_ customer value. The argument that refactoring the code provides value because changes are easier is fundamentally wrong. That's not to say that the overhead work of refactoring isn't both essential and mandatory, but it is not value-added work. It's necessary overhead.
To be honest, much of this confusion comes from Jira, which calls its tickets "stories." A ticket is not a user story. A ticket describes your work, not your users'. Scrum is also at fault. Scrum talks about PBIs, not stories. A PBI is indeed any random bit of work, but to me, putting a non-story PBI onto the backlog is a dangerous practice that leads to delivering no real value at all for extended periods. Calling all PBIs "stories" is ignorant at worst and misguided at best.
This 👇 is exactly correct. "User Story" is not a code word for a developer task or some random bit of work. It is literally the user's story. It describes their work, not yours. It identifies domain-level problems that need to be solved. The entire point of using the user's story is to guarantee that we are continuously adding _user_ value to the system. Logging in is NOT a domain-level problem. It is not work (in the domain) that your user performs. Therefore, it is not a story. Your user logs in to do something else. That "something else" is the story.
So, yes, if you need a login, you add it as an adjunct to implementing a real story. However, like everything else agile, you would NOT do that as a vast pull-out-the-stops, every-bell-and-whistle login system. You'd implement exactly enough that you can move the work forward. That might mean delegating the entire thing to Google or to httpd. You can then incrementally refine the login capability over time if another story requires it. If you have 10 users, you don't need much of a login. As you acquire more, adapt the system to accommodate.
When coding or designing, starting with a framework strikes me as putting the cart before the horse. Create the logical architecture first, then pick frameworks that help you realize that specific architecture. Even then, there's a significant risk of the framework introducing accidental complexity that can significantly increase build and maintenance time. You want exactly as much architecture/code as you need to get the work done. Frameworks often introduce complexity to support a general case that you do not have and often don't do something essential, which requires considerable extra complexity to work around.
To me, it's a sign of cultural dysfunction if a "junior" dev is forced to work alone in isolation, thereby requiring that their work be "checked" by some "senior." After-the-fact checking adds dependencies and delays and don't really work when compared to a culture of quality executed collaboratively. Just because PRs work in a zero-trust isolated-individual-worker open-source environment doesn't mean that it makes sense in any other context.
I chased a bug in a new feature yesterday. It's been a while since I had to fix a problem that escaped TDD. My tests were thorough, I thought, but my implementation had a flaw that I only saw in my manual UI testing. (It turned out to be a pre-optimization.) Finding the cause took a couple hours, a head clearing nap, and some experimentation. It's been months or longer since I had to chase a problem for more than a couple minutes.
I make mistakes all the time while programming. They would be most easily counted in mistakes per minute. (In life I make mistakes too, that's a human thing.) The small steps of TDD, and its cause and effect feedback loop, usually put the mistakes in my face.
In my 20 years before TDD, I had to find all the mistakes in a much longer and troublesome feedback loop. I call that Debug Later Programming. You write some code and debug it later. (You can read my comparison of TDD and DLP here: https://t.co/1wUUh5sSgw)
I keep hearing people talking bout not having vague requirements, but vague is at the heart of agility. You want things as vague as possible until the last responsible moment. The requirements NOT being vague is a huge red flag, and detailed up-front requirements lead to software that's riddled with bugs of the "nobody needs this" and "this isn't the right way to do it" variety.
TDD. Double. Entry. Bookkeeping.
Say everything twice in such a way that the two cross check each other.
Would you trust an accountant who smirked at that idea? If not, why would you hire a programmer who did?
I just cannot understand all the sturm und drang asociated with shoehorning stuff into a Sprint. All that estimation and other nonsense is a collossal waste of time. Just work on the most valuable (to the user) thing. When you're done with that, work on the next most-valuable thing. How hard is that?
– We don't write tests.
– Why?
– Because we don’t have time for it.
– Why?
– Because there is too much work and pressure.
– Why?
– Because we don’t move fast enough.
– Why?
– Because changing software has become difficult and risky.
– Why?
– Because we don’t write tests.
Replacing legacy systems is always a process of drawing a circle around what you're going to replace, then making the replacement better & better. At MO, we work hard to be able to draw small circles, giving us better risk management, building trust, & prioritizing value.
Thinking about architecture: When you know that you will need to change something (but you don't know what), you will go about building in a different way. In other words, the thing you build will be different than it would have been if you weren't thinking about changes. 1/
People conditioned to tolerate spurious test failures ignore significant, real failures. If “some tests always fail,” are we sure it’s only the "bad ones"? Once we learn to ignore the tests, we have negated the value of having tests. https://t.co/xPn8yIxmfF
I'm noticing on many projects that no one has the time to write tests. They feel pressured for time and refuse to spend even a minute writing tests.
And yet, magically, everyone has unlimited time to waste in a debugger! Where is that free time all of a sudden coming from?
@alexbunardzic "Our programming problem has been transformed from “give me multi-currency” to “make this test work, and then make the rest of the tests work.” Much simpler. Much smaller scope for fear. We can make this test work."