Principles - raisercostin/software-wiki GitHub Wiki
Design is a battlefield of multiple acting forces that each drags/push/force the design in a specific way. One some we need to act immediately for some we let them to build pressure and will act later when the decision becomes clearer. Nevertheless is good to understand the stressors.
Design can be learned organically: first learn some fixed forms, after that get rid of them.
The fixed forms are guidelines, principles, philosophies, paradigms, patterns, anti-patterns, idioms, code smells, rules of thumb, adagios, mantras, best practices etc. Some of them applies to the way things are (items and the relationship between them), some of them talk about a process or thinking process (how to move from one state to other). The first are called design principles, the later process principles.
The following is a list of principles that are the highest kind of rules that can guide a novice on resolving situations/tendencies of any type of computing entity: code, components, packages, containers, systems.
- a style of development where you first clarify the intention: what is given, what you need and only then you focus on how (if is really needed)
- functional is more, is intention only - because implementation is implemented once in the engine and is hidden from sight
- see SQL, CSS selectors, regex, map/reduce functional collections (hadoop, spark, etc)
- imperative could be intention first if the method is first defined
- TDD (Design, Development) also focuses on intention first
- focusing on intention first you have a good chance of getting rid of all accidental complexity
- Corollary:
- Do not add accidental complexity/data (noise)
- enables composable features
- http://principles-wiki.net/principles:principle_of_least_surprise
- https://en.wikipedia.org/wiki/Principle_of_least_astonishment
- A special form of breaking/violating PLS is puzzlers
Please do not write puzzlers and do not fall for the game of understanding them except once when you convert them to proper, explicit behavior.
Notorious languages that are breaking PLS:
- bash - for a sane subset see
- perl
- javascript - for a sane subset see JavaScript: The Good Parts - Douglas Crockford
- http://principles-wiki.net/principles:keep_it_simple_stupid
- Connected concepts: Over Design, YAGNI
Fix bugs in one place, future reuse and improvements done in one place.
- http://principles-wiki.net/principles:don_t_repeat_yourself
- remove magical values (use constants that you can use everywhere). They should represent intent (not the actual value), meaning there might be multiple constant names for same magical value each of them representing different intention.
- magical value 1. what is a good name for it?
- magical value 0. what is a good name for it?
- magical value 400. what is a good name for it?
- magical value "EUR". what is a good name for it?
- http://principles-wiki.net/principles:fail_fast
- https://en.wikipedia.org/wiki/Fail-fast
- https://www.martinfowler.com/ieeeSoftware/failFast.pdf
A part(component, class, api, schema, server) should be Open for extension, Closed for modification
- https://en.wikipedia.org/wiki/Open/closed_principle
- http://principles-wiki.net/principles:open-closed_principle?s[]=open&s[]=closed
- original paper by Uncle Bob - https://www.cs.duke.edu/courses/fall07/cps108/papers/ocp.pdf
- http://www.oodesign.com/liskov-s-substitution-principle.html and
- https://dzone.com/articles/the-liskov-substitution-principle-with-examples
- https://martinfowler.com/bliki/InversionOfControl.html
- https://martinfowler.com/articles/dipInTheWild.html#YouMeanDependencyInversionRight
Premature Optimization is the Root of all Evil Optimization is done after a bug is filled with proof that the new code is improving the situation. Use a profiler Connected with YAGNI. Michael A. Jackson
- The First Rule of Program Optimization: Don't do it.
- The Second Rule of Program Optimization – For experts only: Don't do it yet.
See also Intro-to-Performance
On software systems in worst case scenario the systems should be able to crash/reboot and continue from where they left.
- http://web.archive.org/web/20090430014122/http://nplus1.org/articles/a-crash-course-in-failure/
- https://www.zuehlke.com/blog/en/is-it-safe-to-let-it-crash/
The uniform access principle of computer programming was put forth by Bertrand Meyer (originally in Object-Oriented Software Construction). It states "All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation".
- https://martinfowler.com/bliki/UniformAccessPrinciple.html
- https://www.quora.com/Why-are-getters-and-setters-increasingly-considered-an-anti-pattern-in-programming-circles
- https://en.wikipedia.org/wiki/Uniform_access_principle
- Implementation
- other names: composite reuse principle
- articles
-
https://en.wikipedia.org/wiki/Composition_over_inheritance
To favor composition over inheritance is a design principle that gives the design higher flexibility. It is more natural to build business-domain classes out of various components than trying to find commonality between them and creating a family tree Composition relation is more flexible as it may be changed on runtime, while sub-typing relations are static and need recompilation in many languages. Some languages, notably Go,and Rust use type composition exclusively.[4] see also composition helpers for each language
- https://refactoring.guru/replace-inheritance-with-delegation
- https://www.matsim.org/docs/devguide/prefer-composition-and-delegation-over-inheritance
- https://www.geeksforgeeks.org/delegation-vs-inheritance-java/
-
https://en.wikipedia.org/wiki/Composition_over_inheritance
This "law" is a little more controversial than others. There
- Parallel: E suficient sa se inteleaga ce inseamna Law of Demeter si impactul asupra designului. Exista forte care complica designul. Exista forte care il simplifica. Daca suntem constienti putem lua decizii mai bune. O paralela ar fi un sistem in care ai masina, jenti, cauciuc, vintil si aer/azot. Daca doar ca sa schimbi din aer in azot trebuie sa schimbi masina sau jentile … ai cam incalcat law of demeter.
- PRO
- CONS
You're right in identifying the importance of applying events in order for them to properly reflect the state of the system. This can be a particularly tricky challenge when dealing with distributed systems and multiple data sources. Here are some techniques to address this:
- Sequence Numbers: They're the most basic way to ensure events are processed in order. Each event gets a unique, incrementing identifier. As long as all components respect the order of these identifiers, they will process the events in the correct sequence.
- Timestamps: Timestamps are another common way to order events. However, they can be tricky to use correctly due to the risk of clock skew and the difficulty of guaranteeing exact synchronization across distributed systems.
- Vector Clocks: Vector clocks are an extension of logical clocks and can be used in distributed systems to figure out the order of events. They provide a partial ordering of events based on causality, meaning "Event A happened before Event B". This helps in resolving conflicts when merging events from different sources.
- Lamport Clocks: Similar to vector clocks, Lamport clocks are a logical clock system used to determine the order of events in a distributed system. It provides a partial ordering of events with the causal ordering property.
- https://martinfowler.com/articles/patterns-of-distributed-systems/lamport-clock.html
- Time, Clocks, and the Ordering of Events in a Distributed System - https://lamport.azurewebsites.net/pubs/time-clocks.pdf
- Global Transaction IDs: In some distributed systems, a global transaction ID is used to order events. This requires more coordination, as every event across the system must be associated with a globally unique ID that also encodes ordering information.
- Kafka Offsets: If you're using Kafka, it assigns each event a unique, incremental ID called an offset. Kafka guarantees that events within a partition are in the order they were written. However, this order does not extend across partitions.
When merging messages from two independent sources, one approach is to use a Conflict-free Replicated Data Type (CRDT). CRDTs are data structures that allow multiple replicas to be updated independently and concurrently, then merged with a guaranteed mathematically correct outcome.
For deeper insights into these topics, you might want to consider the following resources:
- "Distributed Systems for Fun and Profit" by Mikito Takada
- "Designing Data-Intensive Applications" by Martin Kleppmann
- "Data and Reality" by William Kent
- The DynamoDB paper from Amazon
- Blogs and articles by Leslie Lamport, inventor of Lamport Clocks
- Resources about Apache Kafka for understanding Kafka Offsets
- Do not destroy/discard information.
- For example when ignoring properties you might ignore important information that is there but is invisible for software.
@JsonIgnoreProperties(ignoreUnknown = true) public class ServiceResponse { public String id; public String code; //public String message; }
- In this case you can use
@JsonAnySetter
with a map.@JsonIgnore public Map<String, Object> dynamic = Maps.newTreeMap(); @JsonAnyGetter private Map<String, Object> getValues() { return dynamic; } @JsonAnySetter public void set(String name, Object value) { dynamic.put(name, value); }
- Sample that breaks
- Bad
}catch(Exception e){ //Do nothing with e }
- Best - preserving information existing in exception
}catch(Exception e){ log.debug("Ignoring exception but still log if we need to start debug",e); }
⚠️ If this exception is a legitimate business error we inform at debug level. We don't want to log normal business situations with stacktraces. On some circumstances we might want to investigate and need to know full details. Think: what if an NPE is thrown in such try? NPE is not a business error but a development error. So in strange cases we need to be able to enable debug and see what is happening. But in the rest business errors we don't need to log anything. - Bad
- After 20years of professional coding (30 of coding) I still discover myself that I'm breaking this while still thinking that I'm cured. This happens at higher and higher levels. The levels are:
- loops and functions - assembler, basic didn't have for,while but goto and labels.
- Use the language instructions.
- basic data structures and algorithms: lists, maps, arrays, stacks, lists, queues, arrays.
- Use the language standard libraries
- advanced data structures and algorithms: multimaps, trees, traversals, graphs
- Use common libraries.
- java: guava, vavr, apache commons
- Use common libraries.
- general problems: configuration, logging, persistence access (database & like), rest api client and server
- domain specific problems/patterns
- Read, understand and use code written by colleagues
- Use libraries used by domain/industry
- frameworks: web, integration
- Use frameworks where others already decided on a selection of libraries that go well together.
- products: email, database
- Use external self contained products with minimal code changes (plugins)
- services: search, mail distribution, hosting
- Use external services/SAAS - no need to invent scalability, maintenance, procedures, etc.
- loops and functions - assembler, basic didn't have for,while but goto and labels.
- https://en.wikipedia.org/wiki/Not_invented_here
- Do not add accidental complexity/data (noise)
- Dumb Code Smart Structures
- Null is not an Object
- Array is not an Object
- Infinite Funds Syndrome (Cost of perfection might be an order of magnitude higher than good enough solution) - The assumption of an engineer that have infinite time & funds to do a perfect software(NASA, Nuclear, Aviation) when a good enough (at a fraction of the costs) will do just fine. Usually engineers that do not keep OCD under control.
- Solution: focus on effort and impact (ROI - Return Of Investment) = Value Gained Or Impact / Effort Quantified in Both Resources and Time to have Perfect Solution (read The Nature of Software Development)
- Maximum Impact is limited, Value is effort or negative costs and includes technical debt, lost opportunities and comes from all stakeholders
- Effort could be infinite
- Programming to Interfaces
- DCSS - Dumb code, smart structures
- FailFast
- Elegant Objects
- Defer Execution - do not compute what is not needed. See functional style.
- No Code > Good Code > Incomplete Code > Wrong Code (good information > incomplete information > wrong information)
- Conservative code (open close principle) Do write code to fix your problem/test and only your problem/test. All the other cases should be "fixed" only if they suffer from that bug.
- Uniform Access Principle
See also Uniform Access Principle: "uniform access principle The uniform access principle states that variables and parameterless functions should be accessed using the same syntax. Scala supports this principle by not allowing parentheses to be placed at call sites of parameterless functions. As a result, a parameterless function definition can be changed to a val, or vice versa, without affecting client code."
- https://colterreed.com/talk-to-the-duck/
- https://en.wikipedia.org/wiki/Rubber_duck_debugging
- now the duck could also be
- your fellow developers on stackoverflow or reddit
- AI - https://chat.openai.com/chat
- YAGNI vs KISS
- magical values