|
And just to complicate things I'm going to chime in to say that not only does fewer lines of code not mean more efficient, the opposite is more often the case, because naïve algorithms tend to take less code but do more work.
Because of that, the framework code is often the best choice, even for performance reasons.
I wrote a B-tree library in C# for holding millions of rows of data in a dictionary. The standard dictionary outperformed it for small sizes (of course) and kept up with it all the way until millions of rows at which point it still wasn't much worse, leaving the only advantage of my b-tree was the storing of the data in sorted order.
Don't underestimate a good framework. Lean on it hard.
Real programmers use butterflies
|
|
|
|
|
I presume to be of the same style as you do but in the last project the bank tried to design everything before starting to code, the idea was that the design would actually be the pseudo code they outsource would code from. They got me to prototype the project, what took me 3 months to complete took a team of 12, 18 months to get to production by transferring the prototype code to a design tool and getting the team to code from the tool.
The prototype was a more stable solution.
Never underestimate the power of human stupidity -
RAH
I'm old. I know stuff - JSOP
|
|
|
|
|
Architecture and code are for me on the same side of the balance, together with testing.
On the other side are the requirements, including user stories and business case.
The balance is in short: WHAT ---- HOW
With ATAM (Architecture Tradeoff Analysis Method) one verifies this balance in different ways,
- Does the architecture fulfill all the requirements?
- Does the architecture have more/less than needed? Why?
- Do the requirements define all (non) functional details needed for the architecture?
- Are there missing requirements that can be derived from the architecture?
(e.g. the ones so obvious that they are not mentioned)
With respect to the architecture, (imho) if a programmer can code it, and a test engineer can create a test specification and the requirement engineer can track the requirements, one may stop specifying the architecture. Yes, iterations will follow to improve readability and maintainability.
If the test specification cannot be derived (or seems odd etc), the architecture is probably missing something, often indicating you need to get back to the requirements / stakeholders.
So there is also an important balance in the HOW part between the architecture and the test specification, that may trigger an unbalance in the HOW - WHAT balance.
ATAM Architecture tradeoff analysis method - Wikipedia[^]
|
|
|
|
|
I had been there so many times that it just screams to me all the balancing only experience taught me. Usually learning materials are not really in touch with reality and all the ways it can variate.
My experience is that:
1 - The first step is to try anticipating the scale and lifetime of a project. This will help avoid unnecessary over or under-engineering. Patterns exist to help us, but they are often not worth the cost and can create a huge technical debt in the team.
2 - Code reuse is a double edged sword. It can both help to prevent bugs, but also create bugs. When dealing with complex systems, creating a single business rules model can be very bad. You start twisting and over abstracting so much that the codebase starts becoming very hard to understand, creates a lot of coupling and unpredictable side effects. Applying DDD here is beneficial as you separate an often "Big Ball of Organized Mess" into smaller more manageable contexts (bounded contexts). That will somewhat generate code duplication, but I believe it to be a good thing as the chances of side effects are greatly reduced and allows different business domains to evolve independently. Of course, this requires some mindfulness on the impact of a change in the big pictures, but scenarios like the one below are so much easier to deal with, that's worth the code repetition (and some times data):
Consider a hypothetical system where we have an employee class which are part of two different business domains:
1 - Payroll Management: For payroll management, you need a model that contains properties like: salary, working schedule, name, address, tax number, email. It should contain methods like calculatePayrollTaxes, calculateNetSalary, calculateIncomeTaxes, updatePersonalInfo
2 - Sales: For sales rules you likely need a whole different approach for the employee rate. For example. In a commissioned scheme, you'd need the following properties: commission rate, name, mtdCost. The methods could probably be much more focused like just having calculateCommission.
Because the P&L report needs to account for the costs of the employee, a change in payroll taxes, salary or work schedule will affect the month to date costs. But that can be kept in its entirety contained within Employee entity of the Payroll Management context, without having a huge class with all rules that involve employees. It makes a lot easier to maintain its business rules, doesn't require and endless number of abstraction layers and has much lower chances of causing side effects. It does require a top level architecture that accounts for changes that may affect other domains (for example, the case of the Sales department getting the update on mtdCost). But that can be solved simply over multiple ways that don't require direct coupling of both business context objects.
Now, with a very slim object the sales context is allowed a lot of freedom to objectively evolve objectively and more business oriented, which provides more value and leads to less conflicts. But let's say that the rules for employee names change (which is a shared and repeated property). So the Peyroll Context changes it to split that property in FirstName and LastName.
The first thing you can notice from this change is that it may impact the Employee under the Sales context. But for sales, it still doesn't care that the employee has different properties for first and last names. If you design this properly, you don't even need to care to change anything outside the employee context. Because you can:
1 - Propagate the change in the exact same way. If you use webhooks, direct rest calls or messaging to do interservice communication, you can simply concatenate those properties when you change your model. Everyone else will get those changes the same way they were getting before. You fulfil your need to change the model and don't change what doesn't need change.
2 - If you use a shared database (or tables so to speak) and monolith approach between contexts, you can still apply a similar approach, although not exactly ideal, but you can have the separation of schemas through a unified ORM layer that will account for changes in a shared data model to avoid conflicts. I would personally avoid that as there are multiple ways to (even within a single database) to keep entities separate as they are not really the same entity within the different business contexts and leverage the concept of Domain Events where all other interested domains can subscribe to, which completely eliminates the coupling trap I have fallen to many times over.
But in the end, if you have a simple project, you can simply create a "Small ball of organized mess", which will ship fast, is still easy and small enough to maintain and also adds little effort for an eventual need of refactor to scale.
Getting the sweet spot right to me, is the real challenge. And that is the thing that only experience has taught me and I really fail to contemplate a more effective way to teach that.
To alcohol! The cause of, and solution to, all of life's problems - Homer Simpson
Our heads are round so our thoughts can change direction - Francis Picabia
|
|
|
|
|
I'm almost on the same page, but I also avoid code re-use when the data is different from a functional point of view.
When 2 areas have different use cases and (functionally) different data sets, but can use the same code initially, I just duplicate the code I need and give it a name that's more appropriate for the specific area. Most of the time, the functions start to drift apart along with new business requirements. By splitting it up initially, I avoid having to add logic to differentiate between both areas, something that typically happens over time, and causes bugs when done poorly.
Also, I align my structure with whatever report / graph / property or other business object that gets identified, because that's where your design changes come from in the first place. For the same reason I avoid purely-technical abstraction layers and objects that have no representation for the end user, except for one specific case.
To me, technical abstractions are only useful as a way to divide work between teams (including external teams which I have no control over) and should be implemented, evaluated and maintained as such.
As an example, you could have 2 teams in a product, let's say UI and backend, and use 3 business critical open source packages for reporting purposes. A good structure would then have 5 critical abstractions under the hood to separate those concerns. In this case 2 layers with some rules that work for both teams, and 3 class encapsulations.
I know I sound extremely pragmatic, but the compiler doesn't care about people and doesn't care about profit, and I care tremendously about both. So that's what a good structure should add, in my personal opinion.
|
|
|
|
|
For me "good architecture" means making something easy to debug when it breaks, because it will break and I spend much too much time trying to debug cleverly architected code.
“That which can be asserted without evidence, can be dismissed without evidence.”
― Christopher Hitchens
|
|
|
|
|
You know you're doing it properly when the architecture and the code are the same thing.
|
|
|
|
|
Is a fake horse my little phony?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Do a gallup poll and find out.
"the debugger doesn't tell me anything because this code compiles just fine" - random QA comment
"Facebook is where you tell lies to your friends. Twitter is where you tell the truth to strangers." - chriselst
"I don't drink any more... then again, I don't drink any less." - Mike Mullikins uncle
|
|
|
|
|
Neigh.
"If we don't change direction, we'll end up where we're going"
|
|
|
|
|
I thought about if furlong time - it could be colt lots of things - mane-ly though, it's equestrian of intent.
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
Now you sea-horse, now you don't
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
Yes. And they require f-acres of grazing and roaming space.
If you can't laugh at yourself - ask me and I will do it for you.
|
|
|
|
|
Q: Why do yo need two shots of Western Covid vaccine, but three of the Russian version?
A: Well... the first is the microchip, the second is the battery, but then, the Russian requires more batteries!
"If we don't change direction, we'll end up where we're going"
|
|
|
|
|
Maybe it makes sense to insist on the solar-powered version?
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
The Johnson & Johnson one?
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
Actually, I was thinking more in terms of Lismore[^].
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
I never actually tried that one. These days there are so many brands...
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
Many brands but far fewer distillers.
Lismore is made by Grant.
|
|
|
|
|
I thought it was more a form of Russian Roulette
|
|
|
|
|
So, have some of your devices seem to have been freshly watered[^] before delivery? Not to worry - it's part of the new economy rate class.
It's also being considered for implementation on most cruise lines (and possibly airlines) as an economical alternative for handling overbooking.
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
Nah, the airlines would simply hang you out to dry...
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
|
That rose could also pass for a tulip!
|
|
|
|
|
It is a widely unknown fact that cows poop tulips in the Netherlands.
I'd rather be phishing!
|
|
|
|
|