White Box Core Concepts
We embrace concepts like Clean Code, Clean Architecture, Domain-Driven Design, Self-testing code, Continuous Delivery, Agile practices, and user-focused, pixel-perfect UI/UX for maintainability, adaptability, and seamless user experience
Core OOP Principles
Object-Oriented Programming (OOP) is fundamental to creating robust, maintainable software. This section covers key principles such as polymorphism, inversion of control, and encapsulation, which help structure code for flexibility and scalability.
Use of polymorphism, appropriate use of inheritance & composition, Open-Closed principle at work. Excessive use of if’s and switches should be avoided (replaced by polymorphic behaviour)
How can we measureHow de we treat variation in behaviour in code? How many “if’s / switches” in code? What if we need to add/change some behaviour? (is code Open for extension / Closed for modification?)
Depend on abstractions (interfaces) not on concrete components. Always program against interfaces.
How can we measureAre interfaces used at architectural boundaries? Do dependencies point from the concrete to the abstract?
Always choose the minimum level of visibility (accessibility) possible for the given purpose – keep things as private as possible. Expose as public only when it makes sense to be part of the object’s public interface (“contract”) and only after carefully considering the alternatives. Getter/setters are not our friends and shoud, whenever possible, be avoided.
How can we measureDoes the code use the minimum necessary level of accesibility for methods & fields? Does the public interface of the object make sense as it’s contract with the rest of the system?
Can we avoid getters & setters? Did we make things as private as possible?Key Concepts of Domain-Driven Design (DDD)
Domain-Driven Design (DDD) provides a set of principles and best practices to tackle complex software systems by focusing on business needs. This section explores essential DDD concepts such as domain modeling, aggregates, repositories, and the isolation of business logic in domain entities.
Domain-Driven Design concepts are used throughout the application, guide the architecture and the structure of the code. Ubiquitous Language is understood by all (dev & business) and constantly refined. Great effort and attention is invested in good names which enter the Ubiquitous Language.
How can we measureAre DDD concepts clearly present in the code? Is DDD understood (at least to a basic degree) by all team members? Is a Ubiquitous Language agreed between dev & bus? Is every notion clear and perfectly understood by all parties?
The business problem to be solved should be clearly and accurately represented in the Domain Model. Great care should be taken to keep the Domain Model accurate as the domain evolves over time. All entities and relationships between them should reflect the way the Domain Expert understands the business. Great effort and attention should be invested in maintaining the Domain Model accurate and up to date.
How can we measureDoes the Domain Model accurately reflect the business problem? Are all business scenarios easily expressed and represented by the use of the Domain Model? Does the Domain Model feel flexible and easily adjusted to new scenarios, or does it become rigid and inappropriate?
The business problem is complex enough so we don’t need additional complexity (technical) when dealing with business logic. Business logic should be clearly encapsulated in the domain (domain entities / value objects, domain events, domain services) and separated from any other concern (communcation, security, persistence, etc).
How can we measureIs business logic clearly distributed in the Domain? Does the complexity of the Business logic clearly represented and managed within the Domain? Are all other concerns (persistence, user interface, security, communication, etc) treated at different levels and outside of the Domain? Is the Domain agnostic of implementation of persistence / communication / etc?
Repositories are domain concepts, representing abstract notions “”object collections””, with no regard for the actual implementation. Since they are Domain concepts, they should make sense to Business people as well (should not be a technical thing but should be backed by a technical thing)
How can we measure
How is presistence handled? Is the Domain logic free from any detail about the actual implementation of persistence?
Are repositories very simple concepts when viewed from the Domain?
Domain Entities should be the core components for encapsulating business logic. They should not be mistaken for JPA Entities (simple DTOs with no brain of their own).
Proper use of immutable Value Objects should be made in all relevant areas. Not all concepts diserve to be Domain Entities, many may be better represented as Value Objects.
Are Domain Entities ‘smart’ objects, capable of handling the business logic?
Is the data (state) clearly separated from behaviour (JPA Entities vs Domain Entities)?
– Aggregates should encompass a number of entities that are surrounded by the same transaction boundary.
– Operations inside the Aggregate should be within the same transaction, operations outside the aggregate should be in separate transactions.
– Communication between aggregates should be done by the use of Domain Events and should be eventually consistent.
– Are Aggregates properly used in the code?
– Is access to its contents managed only via the public interface of the Aggregate?
– Are operations outside of the Aggregates handled via Domain Events or is one transaction spanning multiple Aggregates (should be avoided if possible)? Are aggregates well sized (not too large)?
Principles of Clean Code and Architecture
This section explores the principles of writing clean, maintainable code, following both Object-Oriented Programming (OOP) best practices and SOLID design principles. Clean Code focuses on creating readable, expressive code that is optimized for understanding, while SOLID principles help structure code for flexibility, modularity, and separation of responsibilities. These practices together lead to a robust software design that effectively separates concerns and ensures a domain model rich with functionality.
– Code should read like a newspaper (title -> summary -> details).
– Writing code should be optimized for the reader – the easier it is to understand by an outsider, the better it is.
– Clean code is self explanatory, self documenting.
– Great care and energy should be invested in good, self explanatory class/method/variable names.
– Does the code clearly express the intent (why) or only the mechanism (how)?
– Can an outsider understand the code at high level, or is going into details necessary?
– Are method/variable/class names well chosen and self explanatory?
– Change should be managed by making sure that a piece of code has only one reason to change.
– No piece of code should have more than one Owner of the business logic (decider of change).
– Are the methods small enough to ensure One Reason to Change?
– Do different units of code adress the needs of different Business Owners (the drivers of the change), or does one unit of code mix different Owners?
– Are business needs for change separate from technical needs for change (e.g database / cacheing, etc) or are they present in the same unit of code?
– When new behaviour is requested from the application, the existing code should not need to change.
– The new feature should be possible to implement only with new code (new components) and minimum intervention in the old code.
– Is variation in behaviour accomplished though polymorhpism or though chained if’s / switch statements?
– How easy would it be to change an existing behaviour (extend the functionality), would it require to change existing code, or would the new code be written mostly in new components?
– How decoupled are the various behaviours in the code?”
– When extending a base class, the subclass should fit perfectly/make sense in all scenarios of the base class (should be a perfect substitute).
– If there are scenarios related to the base class which do not perfectly fit the subclass, inheritance is not a good fit.
– Are all messages passed to a base class treated appropriately in the subclass?
– Does the subclass fit in the place of the base class in any of the base class’ scenarios?
– Classes should not depend on things they do not need. Interfaces should be split (segragated) in order for clients to depend only on what’s relevant to them.
How can we measure
– Do classes depend on interfaces that are relevant to them, or do they also expose non-relevant methods?
– Could the interface it implements be split into 2 or more cohesive sub-interfaces (which make sense logically)?
– High level modules should not depend upon low level modules. Both should depend upon abstractions.
– Abstractions should not depend upon details. Details should depend upon abstractions.
– Does the flow of dependencies oppose the flow of control (do implementations depend upon high level abstractions)?
– Are abstractions (usually interfaces) well defined as to be depended upon by low level details? Do these abstractions belong to the high level modules (part of the “”contract”” that the “”provider”” is implementing)?”
– Concepts which are closely related belong together (cohesive).
– Concepts which are not closely related belong separately (decupled).
– Are more distant concepts decoupled enough?
– Are highly related concepts close enough?
– Are components the right size?
– Are interfaces used enough?
– Do interfaces belong to the right component/package?
– Adhere to REST API best practices, properly represent Resources, make use of HTTP verbs and appropriate response codes.
– Great care and energy should be invested in designing an API, the URI, verbs, body and response codes.
– Does the API follow the best practices regarding REST APIs?
– What is the Resource here? Are the verbs used appropriately?
– Can we find a better response code, to more closely suite our needs?
– Is the body of the request/response well formed, does it allow for future extention?
– Concerns of the application should be well distributed on different layers / services.
– Objects of the Domain should be rich with functionality (“smart”) and contain all the business logic.
– Does the application have a rich (and smart) Domain Model, making use of OOP principles or does the code appear to have a Domain Model, only to find out that the Entities are little more than DTOs and all logic is performed in heavy, “Application Service” methods in a procedural manner?
Links– Proper use of design patterns, where they are fit for purpose.
How can we measure
– Are there no patterns used in the code?
– Or perhaps are there patterns used for the sake of the pattern, without any real benefit to improving the quality of the code?
Collaborative and Improvement Practices
This section covers practices that promote team collaboration, learning, and continuous improvement. Techniques such as code reviews, pair programming, and Test-Driven Development (TDD) strengthen code quality and shared knowledge. Experimentation, team autonomy, and Continuous Delivery support an adaptive workflow, encouraging ongoing refinement and effective, resilient development processes.
– We work constantly to become better at our craft, to be better versions of ourselves compared to previous years. This is both fun and very useful in our careers.
How can we measure
– What have we learned in the past 6 month?
– How much time can we devote to learning?
– Where do I get my sources of learning?
– Code review as an instrument of learning, feedback, not just control.
How can we measure
– Is code review performed every time a feature is merged into master?
– Code review should be performed across the team (each member having the chance to review everyone’s code) and not hyerarchical (seniors review juniors as a means of control)
– Core review comments should be labeled for severity (nitpick/thought/suggestion, etc)
– Two (or more) programmers, working at the same keyboard & monitors, thinking about the same problem only at 2 different levels: detail / general. A great deal of information, context, experience is exchanged between the two.
How can we measure
– Is pair programming allowed? Is it incouraged or discouraged?
– Do team members feel free to ask for help / “pair up”?
– The team should have enough autonomy to make own decisions about how to organize in order to deliver best performance.
How can we measure
– Can the team choose how it organizes itself?
– Are they allowed to experiment with different solutions and measure the results and adapt?
– The team should periodically look for ways to improve its performance, to improve the way it works and the way people are collaborating.
– Use this introspection to come up with experiments and run these experiments in hope for improvement.
– If the experiments show no improvement, drop and integrate feedback into a new experiment.
– Is the team free/encouraged to experiment?
– How are mistakes/failed experiments regarded by management?
– The software should always be in a releasable state.
– The deployment pipeline takes care of all checks of “deliverability” and is an invaluable instrument of feedback.
– Is the code ready to be deployed (from a technical perspective) simply by the successful completion of the deployment pipeline, or are additional [manual] steps and validations required before it is considered releasable?
– How much of the deployment process is automated?
– Tests should come first, before any line of production code is written.
– It is more than anything an instrument for feedback into the quality of the design of the code.
– Do tests cover all usecases?
– Is code written in such a way that it is easy to test? Can it be tested in isolation, or is extensive use of mocks necessary?