Showing posts with label mental model. Show all posts
Showing posts with label mental model. Show all posts

Friday, October 06, 2023

DevOpsDays Madrid: The Complexity Trap Reevaluating our incentives

 I've had the pleasure of being invited to give a Keynote at the DevOpsDays Madrid event.


I'm leaving the slides and some interesting related references here.

The Complexity Trap Reevaluating our incentives


Video (Spanish):

Youtube video

Thanks to Autentia for the recording of the session.
You can watch the sessions of the conference and other conferences on their channel https://www.youtube.com/@AutentiaMedia 


Slides:

Original document

Related Quotes:

  • "Art is the elimination of the unnecessary." Pablo Picasso
  • "Code is cost, and production code is by far the higher cost.  If you can write more support code (tests, automation, developer tooling) in order to write less&better production code more smoothly, Win!" @jessitron
  • "Code is liability. To build something is to be responsible for its existence, forever. Write more software only as a last resort." @mipsytipsy
  • "Complexity is what makes software hard to change. That, and duplication." Ralph Johnson
  • "Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it." - Alan Perlis
  • "If you don’t actively fight for simplicity in software, complexity will win…and it will suck." Henrik Joreteg
  • "irreversibility was one of the prime drivers of complexity" Enrico Zaninotto
  • "Keep it simple, make it valuable, build it piece by piece" Ron Jeffries
  • "My position is that code, pretty much exclusively, is cost. It’s all liability. The asset is what it does. So, if I can get simpler code that does the same thing, I’ll take the simpler code. If I can have no code and get the same capability, I’ll take no code. No code is better than some code, some code is better than lots of code!" Dan North @tastapod
  • "Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it. And to make matters worse: complexity sells better." Edsger W. Dijkstra
  • "Simplicity is the ultimate sophistication"  Leonardo da Vinci
  • "Simplicity--the art of maximizing the amount  of work not done--is essential." Principles behind the Agile Manifesto
  • "Software maintenance is not "keep it working like before." It is "keep being useful in a changing world"" Jessitron
  • "The art of programming is the art of organizing complexity." Edsger W. Dijkstra
  • "The biggest cause of failure in software-intensive systems is not technical failure; it’s building the wrong thing." Mary Poppendieck
  • "The faster you build crap the more crap you get" Jeff Patton
  • "The old truths: Keep it simple, Make it small, Make it correct, Fight complexity" Joe Armstrong

Related / References:

  • The art of destroying software (Greg Young)
  • Simplicidad para desarrolladores (Spanish)
  • Basal Cost of Software
  • 8 lines of code
  • Complexity Is Outside the Code 
  • Simplicity, The Way of the Unusual Architect 
  • Simple-Made-Easy/

  • Wednesday, August 24, 2022

    Lean Software development: The art of postponing decisions

    One of the seven principles of Lean Software Development is Defer Commitment. This principle tries to maintain options open (options have value) and avoid waste by only solving the current problem (avoiding speculative design). Following this principle allows us to generate easy to change, minimal systems with low accidental complexity and minimal features (as we have a low lock-in).

    Keep in mind that software is only a means to an end, and that we are trying to generate impact, not software.

    Over the years, I have found that postponing decisions and working in small, safe steps have been the key to creating sustainable solutions.

    My experience tells me this is an excellent way to work, at least for product development. 

    This article is based on my experience over the past 12-13 years. All of the companies that I talk about are product companies, some of them startups (TheMotion, Nextail, ClarityAI) and others more classic companies (Alea Soluciones).

    What we mean

    Postponing all technical decisions until the last responsible moment. This practice implies scheduling irreversible decisions for the last responsible moment (LRM) and trying to make as many reversible decisions as possible.

    What we do not postpone

    For the last 12-13 years, I have been trying to work in an agile way, which implies that there are certain decisions in the way I work that I do not postpone, Agile, continuous delivery, eXtreme Programming, etc.

    Some decisions are difficult to postpone. I think the language (ecosystem) and the approach to development (objects vs. functional) should not be postponed and must be clear from the beginning. 

    Some of these decisions may evolve over time, but we must have a solid starting point.

    Working with technical excellence is another decision we do not postpone. It is implicit in my decision to work with Agile, but I think it is important to make it explicit.

    Why we want to postpone / Benefits achieved

    As we will see in the article, postponing decisions is not easy and requires practice. But as I mentioned in the introduction, it allows us to make sustainable and easy to evolve products.

    • A later decision will give us a greater understanding of business and technology.
    • Simpler and smaller solutions
    • Less work :-) (less basal cost  and less time implementing things we don’t need)
    • More likely to not do anything that doesn't bring real value now
    • More likely not to over-engineer
    • Less effort in redoing work if necessary
    • Our goal is to have a lot of real options... react well and fast to anything... and without fear...
    • The more we postpone, the less we fill the backpack, and the less unnecessary stuff we add (less waste).
    • We all know that it’s easier to move with fewer things in the backpack.
    • Good architecture allows us to postpone important decisions, but this does not mean we are obliged to do so. However, being able to postpone them gives us a lot of flexibility.

    This approach is aligned with the lean principles of eliminating waste and the agile principles of postponing until the last responsible moment (LRM) and is one of the approaches that can help the creation of good architecture. In turn, good architecture makes it easier to postpone other decisions.

    How we do it

    It is very difficult to explain everything that needs to be taken into account in order to postpone decisions/commitments without risk and achieve the benefits mentioned above. Anyhow, here is what has worked for us:

    • We postpone decisions in a conscious and meditated manner.
    • We consider a decision to be a good one when:
      • It compromises us as little as possible
      • Allows us to postpone other decisions
      • It is easy to change/revert
      • Attacks a current (not future) problem
      • Good enough (no over-engineering, neither ours nor other people's)
      • No Megaconstructions
    • We reuse libraries but not Frameworks (we don't let them use us)
    • We consider that everything can change (code/design/process)
    • We work in a very iterative manner (See https://productdeveloper.net/iterative-incremental-development)
    • When we start a new feature, we focus on identifying what we need to change in our architecture and our understanding of the ubiquitous domain/language.
    • We assume that what we decided was the right thing to do at that time and in that context, and if it needs to be changed, that's fine.
    • We put a lot of effort into good vertical slicing for the features (with the Hamburger method for example) and even technical slicing to be capable of working in small safe steps.
    • We strive for technical excellence (Clean Code, Simple Design, TDD, Evolutionary Design, etc).
    • We use DDD techniques and Hexagonal architecture.

    The points I mention are not necessary, but they can serve as inspiration for any team that wants to gain a deeper understanding of agility.

    What we need to make it easy to postpone

    • Safety/Confidence: about our system and code and at the team level (psychological safety)
    • Fast and continuous feedback: Customer feedback, System feedback (monitoring, observability), Development feedback (automatic tests, continuous code review with pairing, etc)
    • Maintainable/Evolvable product/code base: We use XP, Devops practices and lean mindset
      • Simple design
      • TDD
      • Continuous refactor
    • Accept/Assume that everything can be changed/removed/evolved (Features, Code, Design, etc)

    We split medium/large changes into small changes, even duplicating code and duplicating data if necessary... all of this in order to be able to postpone decisions, not impede deployment and make incremental small, and safe changes.

    Some examples

    In this section, I will describe some examples of systems in which we have had a lean approach trying to postpone all possible decisions until the last responsible moment. This has allowed us to have a very low time to market and to adapt the solutions to the feedback we were getting all the time.

    Software for small telecommunications networks (up to 5000 subscribers). New fiber optic provision software with a very aggressive time to market:

    • First rollout version with python serialization to file as persistence 
    • After one month, we added a python text search to the persistence layer
    • After two months, we migrated the persistence layer to Redis to improve performance
    • 5 or 6 months later, we migrated the persistence to MySQL with Full-Text Search once we saw the growth trends
    This approach allowed us to roll out a new product line in less than two months and evolve the product during the last ten years.

    At TheMotion, we decided to create a command line application to deploy, check status, create new services, and other common operations. The challenge was to make this application available as soon as possible and evolve it while changing TheMotion's system environment/infrastructure. Another important requirement was the security of access to the application.

    • The first version was implemented as a shell script running in a server and executed by the developers via ssh (authorization, encryption at transit) using an alias.
    • In less than a month, this shell script was migrated to python.
    • And after several months, we removed the ssh call with a python client.

    The developers' command executions remained largely unchanged during these months. In the background, the deployment, the infrastructure, and the running platform were rapidly evolving.


    Also at TheMotion, we were creating a SaaS b2b product from scratch, and in the beginning, we didn’t had customers and the access was using an API. So we took a lean approach to user management, access, and role management.

    • The first version only was usable by a few concrete companies, and we didn’t need to bill anything, so we only restricted access via IP address.
    • The next step was to generate an ID and API Key for each customer and differentiate them by the API Key.
    • After three months, we manually created users (name, email, etc), but without password, only the API Key.
    • The next step was to allow some users to impersonate others (for example, an agency using the SaaS for several final customers).
    • I don’t remember when, but later we created an admin website, and at this moment, we included passwords for the users.

    You can see more examples and strategies in this blog post System: Control its evolution / or be its slave  

    I am also working on a workshop to practice how to defer commitment that will have more concrete examples.


    Problems/Sensations generated by postponing decisions…

    Certainly, postponing decisions is difficult since it generates feelings that we do not usually like:

    • Uncertainty
    • Anxiety
    • Conflict (as engineers)

    Studies show that uncertainty generates fear and anxiety, so it's not surprising that we try to avoid it. Additionally, as software engineers, we have been trained to solve problems, which is why postponing decisions generates conflict.

    Once you get used to working in small steps, with high quality and continuously iterating solutions, much of that anxiety and uncertainty disappears, and development becomes a continuous flow of change.

    Conclusions

    Postponing decisions and commitments is not easy, it’s difficult, and sometimes it can be confused with laziness or unprofessionalism. It couldn't be further from the truth. Professionalism is about solving current problems (YAGNI) with the most straightforward/simple possible solutions (Simple Design, pragmatism) rather than speculating about the future.

    As we have seen, although difficult, postponing decisions/commitment until the last responsible moment has great benefits.

    So, Postpone... to infinity and beyond…



    Until the last responsible moment!!!!

    References and related resources:

    Real options 

    Related blogposts

    Examples

    Practices & Techniques


    Thanks

    The post has been improved based on feedback from:

    Thank you very much to all of you





    Monday, April 18, 2022

    Systems: Control its evolution / or be its slave

     When we develop software (digital products), we usually deal with complex systems. We are not prepared (by definition) for emergent behaviors of these types of systems. As a result, it is hard to predict its future evolution and very easy to lose control over it over time.


    If we don't control complexity, it will become increasingly difficult to adapt the system to what we need, and we will move from developing and evolving the system to spending more time reacting to it as it evolves. We will become slaves to the system rather than controlling its evolution. We will be spending our time working on production incidents, scalability improvements under pressure, and being in a firefighting mode instead of adding value to the product in a sustainable and continuous manner. Needless to say, the business impact is terrible (poor efficiency, unhappy customers, difficulty in defining strategy, etc).


    In my experience, we can keep the system under control by using appropriate tactics.

    Tactics to control system evolution

    I have used the following tactics to control the evolution of systems I have worked on:

    • Good basic hygiene: automated tests, minimum observability, product instrumentation, etc.
    • Simple design based on current needs: Using incremental design and lean product development we can develop simple solutions that fulfill our current needs but allow us to evolve the system in the future.
    • Evolutionary architectures: Invest in an architecture that can evolve and that allows us to postpone decisions (reversible decisions, fitness functions, observability, etc.).
    • Acceptance criteria: Make the acceptance criteria explicit, so that we all know what we consider sufficient hygiene (testing, observability, performance, etc.).
    • Define product limits: Define product limits that allow us to convert problems commonly considered "technical" to business/product problems. For example, instead of talking about scalability as an abstract concept, concrete it at the product level with clear limits (number of concurrent users/requests, maximum response times, etc).
    • Look for boundaries and use them to assign clear ownership for each identified part. The concepts of bounded contexts of DDD and value stream can assist in identifying such limits within the system. Having clear ownership of each part of the system is fundamental since we must remember that we work on complex socio-technological systems.
    • Work in small safe steps: Controlling the evolution of the system is complex and requires us to constantly rethink and adapt. We can only accomplish this if we work in small safe increments of functionality that allow us to change direction without leaving features half-integrated.

    The use of these tactics is usually adequate, but as with everything in our profession, it depends, there are times when a little upfront planning and attention to future needs would be beneficial. See "It depends" / The development Mix (Product, Engineering, Hygiene.


    Below I include several examples of how I have utilized some of these tactics to control the evolution of the system and not be at its mercy.

    Examples

    System Load / Scalability evolution



    Having performance or stability/availability problems when a system is under high load is very common. The most common approaches for this evolution dimension are:

    • Doing nothing and reacting when we discover the first problems (potentially damaging the relationship with our users due to production incidents).
    • Solving “imaginary” scalability problems by preparing/designing our systems for a load that is not real and generating a high waste. Sometimes this is the default option chosen in engineering simply for the pleasure of solving an interesting problem.

    I think that there is a more sensible approach that allows us to control evolution without generating waste (See Lean software development). In this case, we need to have some metrics about the current load and a rough idea of how much maximum load our system can support (without degrading).

    With this information, we can define a soft limit (Product Limit) for the load, as the maximum supported load minus a threshold that gives us time to evolve the system to support more load.

    As we can see in the example diagram, we receive a notification at t1 informing us that we reach the moment to improve the scalability of the system. Between t1 and t2, we improve the scalability to support a new load maximum and redefine the new soft limit and repeat the process when the limit is reached (t3).

    Storage volume growth



    When we store information it is very common that the associated growth of the information volume reaches a point when the technology selected is no longer valid or that we can start to have some problems (high cost, backup problems, etc). 

    As in the previous example, we can do nothing and react after we start to have problems or use a tactic to control the evolution/growth.

    In this case, if we need to maintain all the historical information, we can follow a similar approach as in the previous example about scalability. But if we don’t need to store the historical information we can create a soft limit to detect when we should start to develop an automated data purge process that periodically removes obsolete information. This approach is very lean because it allows us to postpone the implementation of the data purge process until the last responsible moment.


    Controlled errors and quotas


    One of the easiest ways to lose control of a system is to have no controlled way of handling errors. When we don’t control errors, we are in risk of having cascade errors and affecting more users. This is one of the most common examples of losing control of the system.

    One of the first tactics that help us to maintain control of a system is to detect unexpected runtime errors, avoid crashing and at least send a message to the user and avoid as much sa possible that this error impacts other users.

    Another good tactic is to define quotas per user (Product Limits) and use these quotas to throttle requests and show a warning message to the user. In the message, we can inform the user to contact support and use this opportunity to get more information about how the user is using the system or even offer a better SLA to the user.


    Committed capacity


    When we work in a multitenant SaaS environment is very common that the resources used by the sum of all of our customers increase very fast. One interesting tactic to maintain the control of the evolution of the system in this context is to define how much capacity (of a resource) we commit to each type of user (Product limits). Even knowing that customers will not fully utilize their full capacity, having these definitions allows us to identify the worst-case load scenarios we could have.

    In the example diagram, we define the different quotas for each type of user (basic, professional, enterprise). With these definitions and the number of users of each type, we can calculate the maximum committed load at any moment and use this information to make decisions.

    This tactic is used frequently by AWS. For each type of resource, they define default limits, and to extend these limits you need to contact support. AWS can use the information from support requests to make very accurate capacity plans and make better product decisions.


    Modular monolith


    Building a system from scratch is not an easy task, and it is very easy to lose control of the system as it grows (more developers / more functionality). The two most common failures are:

    • Growing a monolith organically without committing effort to modularization and ending up with a big ball of mud (http://www.laputan.org/mud/).
    • Incorporating a microservices architecture when it is not yet time to scale greatly increases complexity without obtaining any of the benefits of this architecture.

    In general, better results can be achieved by developing a monolith, but organizing the code and data into internal modules (modular monolith). As we become familiar with the system and domain, we organize it internally into modules (in alignment with bounded contexts), so we can maintain control of the architecture throughout its evolution. 

    When we see the need, we can extract some of these modules into independent services.

    It is essential to create mechanisms that help us maintain this modularity during evolution (to perform architecture tests to avoid not allowed dependencies between modules, to use different schemas in the DB, to analyze dependencies periodically).

    In addition, we can use these modules to assign ownership of each to a team, allowing each team to work independently.

    Conclusions

    When we lose control over the evolution of the system:

    • we become inefficient and fail to create value.
    • we lose customer trust (incidents, errors, etc).
    • we react to problems instead of being able to follow our product strategy.

    The business impact is huge. Therefore, it is essential to manage complexity and keep evolution under our control.


    The tactics I discuss in this article are quite simple and focus on detecting needs early enough so that we can react in a planned way (rather than reacting under pressure). Moreover, they do not require large investments, in fact, they allow us to have a lean approach and avoid over-engineering. But they do require good hygiene practices (testing, observability, reasonable quality, etc). 


    References and related content:

    Thanks

    The post has been improved based on feedback from:

    Thank you very much to all of you





    Wednesday, October 27, 2021

    "It depends" / The development Mix (Product, Engineering, Hygiene)

    We already know that is everything about context. I read a lot of blog posts talking about how much time a team should use for decoupling components, introducing a new cache system, or improving the scalability of their systems. When reading this type of content, I always think that they are completely right and completely wrong. Everything in our profession depends a lot on the context (the moment of the company, the business strategy, the market traction, etc.).

    I use a mental model that helps me classify the work we do, which allows me to communicate and make decisions. I call this mental model "The Mix".

    In "The Mix", I classify the work we do as product engineers in:
    • Normal product development.
    • Implementing the Engineering Roadmap.
    • Basic hygiene work.

    Normal product development

    Normal product development should be the most common type of work for a Stream Aligned team. It should help to fulfill the mission of the team. It can be composed of new feature development, discovery experiments, feature evolution, etc. I prefer a very lean approach for this work, following agile development methods such as XP or Lean Software Development. It is essential to generate the expected outcomes with the minimal amount of code possible and a good internal quality that minimizes the maintenance cost. Following YAGNI, KISS, Simple design is the perfect approach for this kind of work. We don't know the future. The most efficient way to work is to have the most simple solution that covers our customer's needs without making any "speculative" design that generates tons of accidental complexity in 99% of the cases.

    Summary:
    • Focus on outcomes for the customer within the business constraints.
    • Evolutionary design uses Simple design and avoids creating anything for future "expected/invented" needs.
    • Use a Lean approach (working in small safe steps).
    • Avoid solving problems that we don't have.
    • High-speed feedback loop.
    • Aligned with the Product Roadmap.

    Implementing the Engineering Roadmap

    In parallel to the product work, it is very common to identify engineering needs derived from the company's engineering strategy. This strategy should prepare and maintain the current and future engineering capability. Examples of this type of work are:
    • Designing the system for fast expected growth (the number of customers, engineering team size, etc.).
    • A technology stack change.
    • A change in the delivery strategy (from On-Prem to SaaS, from Web to mobile, etc.).
    • Prepare the architecture to enable work in autonomous teams.
    • This kind of work usually affects several Stream Aligned teams simultaneously and requires coordination at the engineering organization level.
    • These initiatives require a lot of investment and should be coordinated with the product roadmap and aligned with the company's general strategy.

    Summary:
    • Focus on outcomes for the internal architecture and engineering processes.
    • Require more upfront effort to design the solution.
    • It can be implemented with an agile approach but based on the initial design.
    • Low-speed feedback loop.
    • By definition, try to solve problems that we don't have (yet).
    • It is aligned with the Engineering Roadmap (coordinated with the Product Roadmap).

    Basic hygiene work

    To develop any nontrivial product, we need to have some practices and development infrastructure that I consider basic hygiene. I'm talking about having a reasonable test strategy, zero-downtime releases, good internal code quality, basic security practices, etc.
    In the middle of 2021, not considering these points above seems simply a lack of professionalism. 
    So the Basic hygiene work includes any effort we make to implement or improve these minimal practices.

    Of course, I am a big fan of product discovery with prototypes, and these, for example, do not have to have the same test strategy. But remember, a prototype that ends up in production, staying in front of our customers for months, is not a prototype. It is a trap.
     


    Using The Mix

    Thinking about these three types of work and separating them helps me be more explicit about the context and the appropriate trade-offs in each situation. For example, suppose we are in a Normal Product Development initiative. In that case, we cannot expect big architecture change decisions to emerge, and it is better to focus on small safe steps that add value. At the same time, we take notes to consider some initiatives to introduce in the engineering roadmap.

    A mature product organization will introduce performance, scalability, and availability initiatives into the product roadmap. In a less mature organization, those needs are likely to be missing from the product roadmap, and it is up to engineering to fight to get them into the engineering roadmap.

    We can summarize the different dimensions in this table:
     
    Product development Engineering Roadmap Hygiene
    Source
    Product
    Engineering
    Team (+Engineering)
    Development
    Small Safe Steps
    Upfront Planning + Small Safe Steps
    Small Safe Steps
    Practices
    YAGNI, KISS, TDD...
    Evolutionary Architecture, Load Testing, Testing in Production, migration planning...
    Clean Code, CI, CD, Zero downtime, Observability...
    Type of needs
    Current needs
    Future needs
    Prerequisite
    Value Delivery
    Very Fast
    Slow
    Very Fast
    Coordination Needs
    The team should be autonomous
    Coordination with other teams
    Coordination with other teams


    If we analyze the current trends in technology using this mental model, some questions arise:
    • How do technologies like PaaS or Serverless influence this Mix?
    • How does working in Cloud vs. working on-prem affect the engineering roadmap?
    • Does it make sense to consider ourselves good professionals if we don't have strong knowledge about hygiene factors?
    • How does the mix change in the different phases of the company (startup pre-product-market fit, scale-up, big tech)? And with the life cycle of the product?
    The interesting thing about mental models is that they help us think. I hope this model is as valuable for you as it is to me.

    Related / Other mental models

    Saturday, December 09, 2017

    "It depends" / Jocelyn Goldfein model for software classification

    Continuing with the idea of knowing the context of our software as the first step to making better decisions (see it depends blogpost). I will explain in this post a software classification that I found very useful.

    This software classification was created/defined by Jocelyn Goldfein in the article http://firstround.com/review/the-right-way-to-ship-software/ and explained at The "right" way to ship software Jocelyn Goldfein - hack.summit 2016.

    The presentation's core message is that there is no single "right" way to ship software, as every release process optimizes for different goals and involves trade-offs. The optimal approach is situational, determined by a company's technology stack/deployment model and business model/mission, requiring a flexible and adaptable mindset rather than adherence to a "religious" belief about one methodology.

    The model classifies an application in two axes:

    • Horizontal axis: Technology stack and deployment model. From very costly to deploy (on-premise, operating systems, embedded software, etc.) to easy to deploy (web application in a cloud PaaS).
    • Vertical axis: Business model and customer type. From costly enterprise-critical software to free consumer apps.

    This classification helps define the cost of making a mistake, the optimal release process, how to gather feedback, and more.

    For instance:

    • Web/cloud deployment allows rapid iteration and testing. Bugs are easier to fix, enabling a more risk-seeking approach ("move fast and break things").
    • On-prem or device deployments are slower and riskier. Bugs are expensive to fix and may severely disrupt customer operations, thus requiring more conservative, predictable processes.
    • Enterprise-grade software often requires predictability over speed. Customers need to plan around releases.
    • Consumer-facing software benefits from great UX and fast experimentation cycles. New features can be introduced continuously without prior scheduling.

    Beta programs are an especially effective tool across all quadrants: they allow for frequent shipping and direct feedback from users who are willing to tolerate early-stage issues in exchange for access and influence.

    Changing release processes is hard, because it usually demands culture change. Engineers tend to become attached to the processes that worked for them in the past, even if the current context requires a different approach. This can lead to internal conflict, especially when companies pivot from one quadrant (e.g., web) to another (e.g., mobile). To navigate this, teams should align on shared values and mission. Processes are negotiable; purpose is not.

    When hiring, prioritize learning mindset over specific methodologies. New hires should understand the current context to avoid blindly applying processes that made sense elsewhere but may not fit now.

    I found this classification very useful for my day to day work. But remember, the context can be different for each part of a large system and also evolve with time.

    According to this classification, these are the systems in which I have been involved:

    Thank you, Jocelyn Goldfein, for this useful classification model.

    References:

    Thursday, December 07, 2017

    "It depends" / context on creating software products (I)


    "It depends" is the standard consultant answer to any question. It sounds like a joke, but in fact, it is an excellent answer.



    If we are involved in creating software products, our day consists of making a lot of decisions. We have to take decisions at very different levels, for various purposes, and with different importance level:

    • Constant micro decisions when developing and designing software (what is the next test? should we remove this duplication? should we divide this class? what is a good name for this method? and for this module? etc.)
    • Constant Architectural decisions about macro design, practices, strategies, etc.
    • Sometimes estimations (or even better, how to split the features into small steps, so we don't need estimations).
    • What are the optimal priorities for the next tasks to accomplish?
    • Wich experiments can we define to validate a hypothesis?
    • Wich technical debt should we pay right now? 
    • etc.

    Making decisions is hard, very hard...



    In my experience good tactics to make decisions in our profession are:

    • Know as much as possible about the context (business, purpose, why you need to decide about this, etc.).
    • Minimize the risk associated (for example pushing for reversibility when possible).
    • Postpone as much as possible (to gain more awareness about the problem, the context or the risk).
    • Simplify to minimize the number of decisions needed.

    And here is the problem. I usually see very little awareness about the context in which we develop the software.

    This lack of awareness is why we can waste a considerable amount of energy discussing dynamic typing vs. static typing, optimize runtime performance vs. developer productivity, should we use cloud/containers/microservices.

    Everyone is right, or everyone is wrong, depending on our point of view.

    If we don't know about the context, the decision is always wrong :)
    So "it depends"!!! (on the context)