piątek, 31 sierpnia 2018

Miscaptured user stories


Every new requirement has various aspects  that we need to take under consideration and it is really easy to omit important bits and pieces. Throughout my career I've came across two mnemonics that were supposed to help you remember all different aspects that one should consider when developing new piece of software: CUPRIMDSO & FURPS. The issue with these mnemonics is that they mean.... nothing and are a bit old hence a bit outdated. So here is a handy mnemonic: 
"No-one wants to work with a MISCAPTURED story"

This tool can be used when capturing a story to see if we understand how to approach particular aspect but also as a definition of done helping to check if we've done all that we wanted, or consciously decide that given aspect is not relevant.

First lets demystify the mnemonic and then we will deep dive into each aspect

M - maintainability
I - installability
S - security, serviceability, scalability
C - configurability, customization
A - accessibility, agility
P - performance, portability
T - testability
U - usability, ubiquity
R - reliability, recoverability
E - evolvability
D - documentation, distributed, deletability

Maintainability, Mobility

There are couple of additional question we need to ask when talking about maintainability:
  1. Does the new requirement fit into current model? 
  2. Can we implement it without huge amount of "if"s?
  3. Are responsibilities of different classes/modules/services well defined?
If you answered 'yes' to at least one of the above questions, rethink the model that you have in your application or the requirement, either will require redesign.

We are leaving in a mobile world, hence we need to think how the new feature will be presented on various hand-held devices. How to fit all we need to fit on the screen?

 

Installability

When it comes to installability, there are two most important questions:
  1. Assuming that you have a one-click-deployment, will it remain this way after implementing the story?
  2. If you have manual steps during deployment, will the story bring you closer to one-click-deployment?
These two points are the most important, but besides that there are still a few scenarios that we need to consider (remember to thing about default and custom installation procedure - e.g. user picks custom directories):
  1. Fresh install - vanilla case, default values
  2. Re-install (the same version)
  3. Upgrade (new version over old one)
  4. Downgrade (old one over newer)
  5. Uninstall

 

Security, Scalability, Serviceability

Security

Security is a really deep topic, I will propose just few more questions to think about when talking about how given story/requirement influences security of our application:
  1. Are we adding any sensitive data to our application?
  2. Are roles currently defined in our application sufficient for the new story?
  3. Are interfaces required by the story secured?

Scalability 

When talking about scalability, I obviously have in mind concept of scaling-out and not scaling-up. Let's briefly cover the difference between these two models:
  • scale-up - bring more computational power (stronger CPU, more/faster RAM, SSD instead of HDD, etc.)
  • scale-out - spin-up more instances of your service, so that they do the computation in parallel
Here are some helpful questions:
  1. Does the story require implementing some complex/heavy algorithms?
  2. How well the story fits our current scaling-out model?
  3. If we don't scale-out (e.g. it is not yet needed), do we have an idea how the story could have been scaled-out?

Servicability 

We are creating a perfect software that always works as intended.... until it doesn't. Then the first thing we usually do is to look into logs and see what has happened. When implementing new story we need to think about information that we want to log (consider various logging levels: debug, info, warn, error). This might seem obvious but often when implementing new story a difference between debug and info or warn and error might be pretty blurred.
I found following question somewhat helpful when deciding if given log should be debug or info:
  1. INFO - contextual information needed to understand what is currently happening in our system, stated in a business language
  2. DEBUG - low level technical information, needed for debugging 
Mind that we are having a trade-off here between verbosity and readability. I advocate to have a readable code, not cluttered with log.debug() every second line. Whenever you want to add a debug log, consider rather adding a test that would check whatever you are afraid of. Having that in mind log.debug() is rather exceptional. INFO logs on the other hand should show you a clear paths through your application, just be reading it you should know what business actions has been taken.
When it comes to distinguishing between error and debug I use such simple  rule:
  1. WARN - we have an invalid situation but application knows how to handle it and does not require admin intervention
  2. ERROR - we've encountered and erroneous situation that might require admin manual intervention
  3. FATAL - it's so bad that application is about to commit "suicide"

A few generic comments about logging:
  1. Enough context information - when your application is processing some messages, put for example an id of the processed message, if user is performing some actions put id of a user, etc.
  2. Log all relevant pieces of information in one line - as a rule of thumb, do not log a multiline messages - it is really hard then to grep all information that you might be needing when investigating given problem
  3. Be friendly to the tools you use to analyse logs - consider following log message "processing of message (id: 123, version: 1) started" and "processing of message started (id=123, version=1)". Both contain the same information but the former one is splunk-friendly. Splunk will automatically recognize that there are variables "id" and "version". It will even allow you to use them in queries (e.g. "'processing of message started' AND version>2 | stats count by id"). Basically think about how you will be then searching through your logs.
  4. Security aspects - who is going to read your logs? Don't log sensitive data.
  5. Log message consistency - especially when two log messages are related e.g. "processing of message started (id=123, version=1)" and "processing of message finished (id=123, version=1)"

 

Configurability, Customizability

When it comes to configurability, think about two aspects:
  1. Things that will change between environments - these certainly needs to be configurable, as before your software reaches Production environment, it will surely go through Continuous Integration environment and probably some User Acceptance Testing (Staging) environment - each of them will need to have different configuration (e.g. different url to a services which our application is using) 
  2. Things that support engineer (you :-)) would like to change in order to tune the application without a need of building a new artefact and going through procedure to deploy new artefact to Production environment (unless in your ecosystem it is really cheap to deploy stuff on Production - then you can be a bit more relaxed about this rule)
Customizability is rather oriented towards user. So how user can customize the way our application looks or behaves.

 

Accessibility, Agility

Accessibility has at least two different aspects:
  1. Disabled people - it is good if given information is conveyed to a user using at least two different ways (e.g. if you want to show an error message to a user you can show a message in a red frame, but what if a user is colour-blind? Hence you might not only put the message into a red frame but also put there a red cross sign)
  2. How easy it is for a regular user to access given feature - A/B testing might be really helpful to assess how easy it is
When thinking about Agility, you try to assess if you are able to deliver the story in an agile way (the fact that you call it a story instead of requirement does not count ;-)). Several aspects are to be taken under consideration:
  1. Team's business knowledge/intuition .vs. Product owner availability - even though it seems at the beginning that you know how to implement/test the story I can guarantee that unexpected questions will arise. Having said that it is important that either team has a great deal of business knowledge so that they themselves are able to answer these questions or Product owner is highly available.
  2. Team understands the value that story delivers so that they are able to come up with a good-enough solution
  3. Story is fairly short so that it fits into an iteration or, if you use kanban, it does not plug your pipeline
  4. Team has enough knowledge to estimate the story (using whatever units)
  5. Team knows how to demonstrate the story

 

Performance

Quite obvious point I think:
  1. Does the story require adding a long-lasting activities (e.g. report generation)?
    • Is there a possibility to do given operation in an asynchronous mode?
    • What is more important latency or response time?
    • Is it possible to scale the problem out?
  2. Does the story affect performance of our application?
    • Do we need to add/amend performance scenario?
    • Do we need to add/amend data used for performance tests? 
    • How users will be using this new functionality (often, once per week/month, etc)?

 

Testability

Two most important things:
  1. Do we know how to test the story on various levels (at the beginning end-2-end would be the most important, but we need to think about lower level tests as well)?
    • Will we need additional resources (e.g. test DB) for automated tests? 
  2. Does the application expose proper API so that we can get all the information we need to assert if it works properly?
The thing that is often overlooked is that fact that in order for an application to be testable it sometimes need to expose additional API for testing purposes. We need to design our application for testability. This rule apply to all levels:
  1. unit - avoid static methods, separate object creation from usage
  2. integration - define clear (easy to cut) contexts with well defined entry/exit points
  3. end-2-end - define business interfaces that are required to assert if application is working properly

 

Usability, Ubiquity

When it comes to usability the question is fairly simple: is our application user-friendly? There are various more fine-grained metrics we can use to assess the user-friendliness:
  1. How efficient it is to achieve a goal in our application?
    1. How much time does it take?
    2. How many clicks user needs to make?
    3. How many screens user needs to go through?
  2. How intuitive is our application? 
    1. How much time user spends trying to figure out how to use our application?
    2. How many screens has the user seen before finding what she wanted?
  3.  How much do users like our application?
Ubiquity is related to the glossary we use in our project. The idea is that the same words should be used to describe business concepts when talking with users, product owner, between ourselves. Furthermore these concepts should be found in the code, both production and test code. This so called Ubiquities Language concept comes from Domain Driven Design and the main purpose behind it is to make communication between various parties easier. Another benefit is that when reading the code we understand the concepts hence it is easier to develop/amend the existing model. Having said that here are the questions that we need to ask ourselves:
  1. Should we add a new definition to our glossary?
  2. Should we amend existing definition?
  3. Should we remove existing definition?

 

Reliability, Recoverability

As I alerady said I know that the software we create is fault-less, however production environment is a dangerous place. Various things may happen:
  1. Server on which our application is deployed unexpectedly goes down
  2. Disc gets unmounted
  3. When trying to log some vividly important message, we run out of disk space
  4. Service which we are using becomes unreachable
  5. Our database that is located on a separate server becomes unreachable
  6. etc.
There are basically two questions that we want to ask:
  1. Having experienced a failure, will our application recover by itself?
  2. Assuming that it will recover, can I relay on the information that will be presented to me?

Evolvability

I really like the division into two different quality aspects:
  1. Quality of a feature - does it fulfil users' expectation?
  2. Quality of a design - is it easy to evolve the architecture further?
When talking about evolvability I have in mind the "Quality of a design". It requires a certain sense of art to develop solution that does not add things that will be hard to change in the future. Here is a very nice talk about Evolutionary architecture.

 

Documentation, Distributed, Deletability

When talking about documentation we need to distinguish three different aspects:
  1. User documentation - it should be treated the same way as any other product we deliver to the customer (when agile manifest talks about "working software over comprehensive documentation" - this is not user documentation that it has in mind).
  2. Architectural documentation - this one should be kept as close to the code as possible as it tends to be outdate and rot when noone looks at it. A good practice is to keep it as readme file in the code repository. It is easy to exaggerate with it so think twice before putting an information to the readme - even though it is close to the code, IDE not always will recognize that when refactoring your solution you also need to refactor your readme.
  3. Self-documenting code - the best book I've read so far about self documenting code is the "Clean code" by Robert C. Martin
    1. Method/Class names - the main idea here is that one should be able to find in a code business domain.
    2. Executable documentation - tests are one of the ways of documenting the behaviour of the software, especially with various BDD frameworks which allow us to write sentences in a natural language which are then translated into executable pieces of test code, which is run against our application.
  4. Project documentation - project charters, high level design, low level design, test plans, test execution S-curves, etc. - keep it as small/short as possible - this is the "comprehensive documentation" that we value less then "working software". Let's be honest, it is needed to some extend - especially if you are working in an environment that needs to go through various Audits - but the goal is to use it, if you create a document which noone reads a red sign should appear in your head.
Most of us are working in a distributed environment hence we need to deal with all sorts of distributed computing problems:
  1. From implementation perspective we need to take care of fallacies of distributed computing
  2. From testing perspective we need to ensure that the contract between two services (provider and consumer) is automatically tested and that we will not accidentally break it - Consumer driven contract come to rescue here
Concept of deletability has been presented to me by Greg Young in his great talk "The art of destroying software" - sounds like a talk for a tester doesn't it 😉. The additional question we ask to ourselves is:
  • Am I able to add the new requirement in such a way that it will be easy to remove it, or will I tangle it into the existing solution? 
There are many reasons why we'd like to be able to quickly/easily remove features, to name just a few:
  1. Features that are easy to remove are relatively easy to understand as they tend to:
    1. have clear interfaces 
    2. be less coupled with the existing solution 
  2. Since they are relatively easy to understand, they are also relatively easy to refactor