• Fred Brooks (The Mythical Man-Month): the communication overhead of a team of size $N$ is $O(N^2)$

Event Sourcing

REQUIREMENTS

  • (McConnell, 2004) A problem definition defines what the problem is without any reference to possible solutions. It’s a simple statement, maybe one or two pages, and it should sound like a problem. The statement “We can’t keep up with orders for the Gigatron” sounds like a problem and is a good problem definition. The statement “We need to optimize our automated data-entry system to keep up with orders for the Gigatron” is a poor problem definition. It doesn’t sound like a problem; it sounds like a solution.
    • Are acceptable tradeoffs between competing attributes specified - for example, between robustness and correctness?
    • Are all the inputs to the system specified, including their source, accuracy, range of values, and frequency?
    • Are all the outputs from the system specified, including their destination, accuracy, range of values, frequency, and format?
    • Acceptance Criteria
  • (Tidwell, 2011) Field research, to find out what the intended users are like and what they already do
  • (Tidwell, 2011) Goal and task analysis, to describe and clarify what users will do with what you’re building
  • (Tidwell, 2011) Enough time to iterate over several versions of the design, because you won’t get it right the first time. Specifically, you’ll want to learn:
    • Their goals in using the software you design
    • The specific tasks they undertake in pursuit of those goals
    • The language and words they use to describe what they’re doing
    • Their skill at using software similar to what you’re designing
    • Their attitudes toward the kind of thing you’re designing, and how different designs might affect those attitudes
  • (Tidwell, 2011) In other words, how much openness is there in the interface? Too little, and your users feel trapped and unsatisfied; too much, and they stand there paralyzed, not knowing what to do next, unprepared for that level of interaction. Therefore, you need to choose how much freedom your users have to act arbitrarily.
  • (Bloch, n.d.) API is a cover story
    • hide impl, info, internal data formats
    • think of concepts rather than code
    • API should do one thing and do it well
    • as small as possible but no smaller
    • API should be easy to use and hard to misuse. It should be easy to do simple things; possible to do complex things; and impossible, or at least difficult, to do wrong things.
    • API should be self-documenting: It should rarely require documentation to read code written to a good API. In fact, it should rarely require documentation to write it.
    • Code the use-cases against your API before you implement it
    • Maintain the code for uses-cases as the API evolves
    • You can’t please everyone so aim to displease everyone equally. Most APIs are over-constrained.
    • When in doubt, leave it out.
    • Minimize mutability.
    • Don’t make the client do anything the library could do.

PLANNING / SPIKE / TRIES

  • (McConnell, 2004) The overarching goal of preparation is risk reduction: a good project planner clears major risks out of the way as early as possible so that the bulk of the project can proceed as smoothly as possible. By far the most common project risks in software development are poor requirements and poor project planning, thus preparation tends to focus on improving requirements and project plans.
  • (McConnell, 2004) If the “box” is the boundary of constraints and conditions, then the trick is to find the box…. Don’t think outside the box—find the box. —Andy Hunt and Dave Thomas
  • (Pilone, 2008) The goal during estimation is to eliminate as many assumptions as possible. So that an assumption becomes a requirement (task).
  • (Pilone, 2008) A BIG user story estimate is a BAD user story estimate!!
    • RULE OF THUMB: Estimates greater than 15 days par user story allow too much room for error => break it into smaller meaningful pieces.
  • (Pilone, 2008) Estimates are all about confidence.
  • (Hunt, 2000) Prototype to learn and diminish risk - What sorts of things might you choose to investigate with a prototype? Anything that carries risk. Anything that hasn’t been tried before, or that is absolutely critical to the final system. Anything unproven, experimental, or doubtful. Anything you aren’t comfortable with. You can prototype.
    • Architecture
    • New functionality in an existing system
    • Structure or contents of external data
    • Third-party tools or components
    • Performance issues
    • User interface design

SYSTEM DESIGN

  • (McConnell, 2004) Error handling
    • where in the system
    • how they will be propagated/ or handled.
    • validations - model/view/elsewhere …
    • fault tolerance (think of CAP)
  • (McConnell, 2004) Scalability
  • (McConnell, 2004) Interoperability
  • (McConnell, 2004) Look for:
    • Minimize the amount of essential complexity that anyone’s brain has to deal with at any one time
    • Keep accidental complexity from needlessly proliferating
    • Minimal complexity, Ease of maintenance, Extensibility
    • Stratification - it means trying to keep the levels of decomposition stratified so that you can view the system at any single level and get a consistent view. Design the system so that you can view it at one level without dipping into other levels. (e.g. if you’re writing a modern system that has to use a lot of older, poorly designed code, write a layer of the new system that’s responsible for interfacing with the old code).
  • (Hunt, 2000) NOTE: The problem with most binary formats is that the context necessary to understand the data is separate from the data itself. You are artificially divorcing the data from its meaning. The data may as well be encrypted; it is absolutely meaningless without the application logic to parse it. With plain text, however, you can achieve a self-describing data stream that is independent of the application that created it.
  • (Newman, 2015) Melvin Conway’s paper How Do Committees Invent, published in Datamation magazine in April 1968, observed that: Any organization that designs a system (defined more broadly here than just information systems) will inevitably produce a design whose structure is a copy of the organization’s communication structure. This statement is often quoted, in various forms, as Conway’s law. Eric S. Raymond summarized this phenomenon in The New Hacker’s Dictionary (MIT Press) by stating “If you have four groups working on a compiler, you’ll get a 4-pass compiler.”

API DESIGN / IMPLEMENTATION

  • (Tulach, 2012) The best API is no API
  • (Tulach, 2012) Everything you leave in an API can be misused.
  • (Tulach, 2012) The less you need to understand the API’s details to use it, the better.
  • (Tulach, 2012) A Factory is better than a Constructor.
    • No need to return the exact class
  • (Tulach, 2012) Do not expose deep hierarchies
  • (Tulach, 2012) Code Against Interfaces (abstract definitions), not implementations.
    • “Is there actually any reason to use abstract classes?” The short answer is “No.” Abstract classes in an API are suspicious and often indicate an unwillingness to invest more time in the proper API design. The longer answer is, “Well, there might be reasons to use abstract classes in APIs after all.”
  • (Tulach, 2012) Separate APIs for Clients (api that people call) and Providers (api that people implement)
    • API evolution is different from SPI (service provider interface) evolution.
    • The path of evolution depends on the type of interface: additions to an API are acceptable, while removing functionality is not. In SPIs removals are allowed, while additions are not.
  • (Tulach, 2012) Prefer immutability.
  • (Bloch, n.d.) if there must be a state, keep it small and known, and document it
  • (Martin, 2009) checked expectations (as the one in Java where they are declared) broke encapsulation, because if a method in a class at low level throws a new exception all calls of this modified function must be rewritten also.
  • (Martin, 2009) define expectations in terms of caller’s needs
  • (Martin, 2009) wrap third-party apis, so they can be easily changed.
  • (Hunt, 2000) Avoid global data.
  • (Bloch, n.d.) API implementation
    • Fail fast. The sooner you report a bug, the less damage it will do. Compile-time is best. If you must fail at run-time, do it as soon as possible.
    • Provide programmatic access to all data available in string form.
    • Avoid return values that demand exceptional processing. Clients will forget to write the special case code, leading to bugs. For example, return zero-length arrays or collections rather than nulls.
    • Throw exceptions only to indicate exceptional conditions. Otherwise, clients will be forced to use exceptions for normal flow control, leading to programs that are hard to read
    • Throw unchecked exceptions unless clients can realistically recover from the failure.
  • Convention over Configuration

(USER) INTERFACE DESIGN

  • (Tidwell, 2011) Here’s an even more fundamental question: how much effort are your users willing to spend to learn your interface?
    1. safe exploration “Let me explore without getting lost or getting into trouble.” Good software allows people to try something unfamiliar, back out and try something else, all without stress.
    2. instant gratification “I want to accomplish something now, not later.”
    3. Satisfying “This is good enough. I don’t want to spend more time learning to do it better”. Make labels short, plainly worded, and quick to read. (This includes menu items, buttons, links, and anything else identified by text.) They’ll be scanned and guessed about; write them so that a user’s first guess about meaning is correct. If he guesses wrong several times, he’ll be frustrated and you’re both off to a bad start. Use the layout of the interface to communicate meaning
    4. changes in midstream “I changed my mind about what I was doing.” What this means for designers is that you should provide opportunities for people to do that. Make choices available. Don’t lock users into a choice-poor environment with no global navigation, or no connections to other pages or functionality, unless there’s a good reason to do so. Those reasons do exist.
    5. deferred choices “I don’t want to answer that now; just let me finish!” Don’t accost the user with too many upfront choices in the first place. On the forms that he does have to use, clearly mark the required fields, and don’t make too many of them required. Let him move on without answering the optional ones. Sometimes you can separate the few important questions or options from others that are less important. Present the short list; hide the long list.
    6. incremental construction “Let me change this. That doesn’t look right; let me change it again. That’s better.”
    7. habituation “That gesture works everywhere else; why doesn’t it work here, too?”
    8. spatial memory “I swear that button was here a minute ago. Where did it go?” When people manipulate objects and documents, they often find them again later by remembering where they are, not what they’re named.
    9. prospective memory “I’m putting this here to remind myself to deal with it later.” In many cases, that kind of hands-off flexibility is all you really need. Give people the tools to create their own reminder systems. Just don’t try to design a system that’s too smart for its own good.
    10. streamlined repetition “I have to repeat this how many times?” In many kinds of applications, users sometimes find themselves having to perform the same operation over and over again. The easier it is for them, the better. If you can help reduce that operation down to one keystroke or click per repetition or, better, just a few keystrokes or clicks for all repetitions then you will spare users much tedium.
    11. keyboard only “Please don’t make me use the mouse.”
  • (Tidwell, 2011) Rather than thinking in terms of windows, tree views, and links, you might think abstractly about how to organize the actions and objects in your application in the way truest to your subject matter. You can postpone the decisions about using specific windows and widgets.

CONCURRENCY

  • (Tulach, 2012) The basic advice for a programmer using a language with threads and locks, such as Java, is do not hold a lock while calling foreign code. If you follow this rule, you eliminate the fourth condition.
  • (Tulach, 2012) In classes that allow subclassing, you should never call a method that can be overridden while holding a lock. Alternatively, you should replace subclassable classes with more interfaces and you should replace delegation with a clear separation of concerns.
  • (Martin, 2009) Objects are abstractions of processing. Threads are abstractions of schedule.
  • (Martin, 2009) Concurrency is decoupling WHAT from WHEN

CLASSES

  • (McConnell, 2004) Hiding more is better than hiding less.
  • (McConnell, 2004) It ain’t abstract if you have to look at the underlying implementation to understand what’s going on.
  • (McConnell, 2004) Joshua Bloch - “Inheritance breaks encapsulation”.
  • (McConnell, 2004) class’s methods not more than 7
    • STM 7+/-2
  • (McConnell, 2004) loose coupling/high cohesion
  • (Martin, 2009) One class - one responsibility - one reason to change
  • (McConnell, 2004) Use enumerated types as an alternative to boolean variables
    • Reserve the first entry in the enumerated type as invalid (i.e. 0)
  • (McConnell, 2004) Use boolean variables to simplify complicated tests
  • (Tulach, 2012) The only real difference between a soft reference and a weak reference is that the garbage collector uses algorithms to decide whether or not to reclaim a softly reachable object, but always reclaims a weakly reachable object.
  • (Martin, 2009) Data structures / Objects
    • Data structures only expose their information and have no behaviour. Other classes control the behviour or business rules
    • The data structure contains information to be used
    • Objects have behaviour to be used, and should not be asked for its internals.
  • (Bloch, n.d.) Subclass only if you can say with a straight face that every instance of the subclass is an instance of the superclass. Exposed classes should never subclass just to reuse implementation code.

METHODS/FUNCTIONS

  • (Tulach, 2012) Method Design : exceptions for exceptional conditions
    • Don’t force client to use exceptions for control flow
    • Conversely, don’t fail silently
    • Favor unchecked over checked exceptions (Avoid unnecessary use of checked exceptions)
  • (Martin, 2009) one function - one responsibility
  • (Martin, 2009) Functions should either do something or answer something, but not both.
  • (Martin, 2009) Try/catch blocks are ugly in their own right. They confuse the structure of the code and mix error processing with normal processing. So it is better to extract the bodies of the try and catch blocks out into functions of their own.
  • (Martin, 2009) Functions should do one thing. Error handing is one thing. Thus, a function that handles errors should do nothing else.
  • (Bloch, n.d.) do not use exceptions for control flow
  • (Bloch, n.d.) do not throw the client into exception handling, use them only when the client must be alerted
  • (Bloch, n.d.) do not return collections

TEST

  • (Pilone, 2008) test driven development - when you write tests before any code, and then lets those test drive your code you are using TDD
    • Rule 1: Your test should always FAIL before you implement any code.
    • Rule 2: Implement the SIMPLEST CODE POSSIBLE to make your tests pass
  • (Pilone, 2008) Each test should do just one thing

References

  • Bloch, J. How To Design A Good API and Why it Matters. https://www.youtube.com/watch?v=heh4OeB9A-c
  • Fowler, M. Event Sourcing. In martinfowler.com. https://martinfowler.com/eaaDev/EventSourcing.html
  • Hunt, A. (2000). The pragmatic programmer. Addison-Wesley.
  • Martin, R. (2009). Clean code : a handbook of agile software craftsmanship. Prentice Hall.
  • McConnell, S. (2004). Code complete. Microsoft Press.
  • Newman, S. (2015). Building microservices. O’Reilly Media.
  • Pilone, D. (2008). Head first software development. O’Reilly.
  • Tidwell, J. (2011). Designing interfaces. O’Reilly.
  • Tulach, J. (2012). Practical API design. Apress Distributed to the Book trade worldwide by Springer Science+Business Media New York.