2019-08-13 21:15

When Is Software Ready?

Is software ready when it's (mostly) bug free? When all promised features are ready? When it offers enough commercial value to be put into use for instance when it’s able to generate revenue? When all the boxes are ticked on a Definition of Done list? When the schedule says so? Or the boss?

The software industry has wound up in a strange place. When is a doctor ready with an operation? A physicist with a mathematical model? Or an airplane engineer diagnosing an engine problem? The universal answer is "it's ready when she says it's ready". Why do we, generally speaking, accept this answer from people in these occupations, but not from software professionals?

Let's explore a list of possible reasons:

Composability fallacy

Even non-developers know that software can be built using a mix of new and existing components. While developers know implementing an existing component in the form of a library or API has its own set of challenges, the availability of components has decreased the perceived complexity of building software. But perhaps the (perceived) benefits of using existing software outweigh the actual benefits?

Similarity fallacy

Chances are that what you are building is not unique. Most people in the team will realize this, management knows it, as well as the customer. Does this introduce the fallacious argument "since it's already built by company X and Y it can't be that hard"? Since no one knows how much effort was needed–and since you can’t benefit from their experience–you should not use this argument,

Misjudging complexity

This is the most commonly heard reason among developers ("if only they knew how complex this is"). A great example of misjudging complexity is translating a website to a second language. For a non-developer the problem is simply "make a copy of the site and ask a translator to translate all the words". But developers know that making software multilingual is probably a multi-week effort.

Dogmatic scheduling

Companies, understandably, schedule meetings, payments, hirings, deliveries, procurements, et cetera. This is convenient, plannable, and professional. It's only logical (albeit incorrect) that people assume software development, being a cog in the big machine, can follow the exact same scheduling process like any other part of the business. Don't get us wrong, we think it's actually possible to achieve a reasonable degree of planning accuracy, provided the scope is small and well-defined, but "reasonable" does not fit everyone's definition of planning accuracy.

These reasons, put together, may lead to a conflicting notion of the state of readiness of software, or how quick readiness can be achieved during the last phase of development.

What can you do to align development teams, management, and customers on acceptance criteria?

  • Compile a list of checks (i.e. release criteria) that the software must pass. The wording should be precise and preferably non-technical. Ensure each check covers a single feature or even better, a specific element of a feature.
  • Make sure all stakeholders agree, beforehand, that this checklist to a large degree defines the state of readiness.
  • Don't wait till the very last moment to apply this checklist. Instead, use it on a daily basis during the development phase to assess how far a project is. Note that a project's timeline is not a straight vector, instead it's more like a bell curve (what Basecamp calls a Hill Chart) so don't fret if progress seems slow in the beginning.
  • Developers should take time to carefully explain non-obvious complexity. Management and customers should explain which business priorities drove the decision to create this software in the first place, enabling developers to prioritize their work. It's not enough to do this during a one-time kick-off meeting. Transferring knowledge takes time, schedule follow-up meetings to keep discussing points of view, and especially assumptions, from both sides.

Conclusion

The decision to release software should be based on mutually agreed criteria. Don't argue about "why are features X and Y not ready?", instead discuss "what is needed to complete checklist items #11, #23, and #74". In this way you keep the readiness discussion practical and devoid of emotion.

Permalink — Comments or kudos? Find me on Twitter.

Are you a project or product manager? Ship better software with Releasewise!


2019-08-02 09:02

Writing Better Ruby (Part 1)

While reading the excellent Ruby Style Guide I came across best practices I often forget while writing Ruby. In order to plasticize my brain, and perhaps help you prevent making the same mistakes, I wrote down ten of them. I'll probably create a follow-up post with additional mistakes.

1. No non-nil checks

Don’t do explicit non-nil checks unless you’re dealing with boolean values – style guide

There's no need to call !obj.nil? or test for nil inequality unless you want to check whether a Boolean value is really not nil i.e. whether it has been set to true or false.

I suspect I sometimes add a useless nil? to make it explicit to the reader[1] that I really mean to test for nil

Ref [1]: I always write code thinking John Carmack will review my code and yell at me – I swear this helps.

For Rails there are additional methods that test for nil such as blank? which kind of softens the "badness" of making explicit (non-)nil checks since they test more than just nil. If you look at blank.rb you'll see that Rails adds blank? to 9 classes. Frankly I've always had a slight dislike of blank? because it kind of says "I don't really know what kind of state this variable is in so I test for a broad set of conditions".

2. Prefer __send__

Prefer __send__ over send, as send may overlap with existing methods – style guide

Since send is often used as a method name it's likely to override Ruby's BasicObject send. If you use __send__ then you take away the ambiguity.

3. No method_missing

Avoid using method_missing for metaprogramming because backtraces become messy, the behavior is not listed in #methods, and misspelled method calls might silently work – style guide

The Ruby Style Guide article explains how to use method_missing if there's really no alternative.

Since Ruby 1.9 there is Proc#curry which can be used in cases where you would otherwise have used method_missing. This article gives examples of curry. The article also explains the difference between partial application and currying.

4. Non-Capturing Regexp

Use non-capturing groups when you don’t use the captured result – style guide

The article gives two examples:

# bad
/(first|second)/

# good
/(?:first|second)/

A good example of using non-capturing groups is extracting the domain name from a URL.

For instance this regex (https?:\/\/)([^\/\?\&]+) yields two match groups:

1. https://
2. example.com

When you turn the first group into a non-capturing group via (?:https?:\/\/)([^\/\?\&]+) it only yields what you're looking for:

1. example.com

Granted, this tip is not Ruby-specific but still it's something I tend to forget.

5. Date & Time

Prefer Time.now over Time.new when retrieving the current system time – style guide

and

Don’t use DateTime unless you need to account for historical calendar reform - and if you do, explicitly specify the start argument to clearly state your intentions.

Although the style guide itself doesn't provide the reason for choosing Time over DateTime, this Gist does.

6. Named Format Tokens

When using named format string tokens, favor %<name>s over %{name} because it encodes information about the type of the value – style guide

The article gives two examples:

# bad
format('Hello, %{name}', name: 'John')

# good
format('Hello, %<name>s', name: 'John')

This left me scratching my head for a second because I didn't know about the "good" way, but it makes total sense now because just like sprintf the s in %<name>s indicates you're dealing with a string. Passing an integer will throw an ArgumentError.

7. Don’t Abuse gsub

Don’t use String#gsub in scenarios in which you can use a faster and more specialized alternative – style guide

You have three choices: tr, sub, and gsub:

tr

tr is the most bare bones one. It stands for translate characters, and is named after the tr command in Unix.

It can do things like:

"hello".tr('el', 'ip') #=> "hippo"

and

"hello".tr('a-y', 'b-z') #=> "ifmmp"

sub

Ruby doc states: [..] Returns a copy of str with the first occurrence of pattern replaced by the second argument.

It can do things like this:

hello".sub(/[aeiou]/, '*') #=> "h*llo"

Nice touch: sub() supports blocks:

"hello".sub(/./) {|s| s.ord.to_s + ' ' } #=> "104 ello"

gsub

This is the Swiss Army knife of string replacement. The docs state: Returns a copy of str with all occurrences of pattern substituted for the second argument.

The Ruby Style Guide makes the case for not abusing gsub because 1) tr and sub can be substantially faster, and 2) it's a good habit to use the smallest, fastest, and most specialized tool available.

I ran a naive benchmark on a 10k line text file to see the difference between the three methods. The sub methods wins (of course, it only substitutes a single character per line in this benchmark), then comes tr, and then gsub. The sub method needed only 17% of the time needed by gsub to process 10k lines.

8. No to_s

Don’t use Object#to_s on interpolated objects. It’s invoked on them automatically – style guide

I find myself making this mistake in my quest to be specific (again, to the reader of my code) but it's unnecessary.

9. Single Quote

Prefer single-quoted strings when you don’t need string interpolation or special symbols such as \t, \n, ', etc – style guide

This is one of the reasons why you should be running Rubocop early and often (and automated) in your projects because repairing this formatting flaw late in the game is a lot of work.

The reason is, I guess, two-fold: 1) you make it clear you're not intending to do string interpolation, and 2) you make it slightly easier for the parser to perform an optimization (it knows it doesn't have the deal with interpolation).

10. String Interpolation

Prefer string interpolation and string formatting instead of string concatenation – style guide

The examples speak for themselves. The main reason why email_with_name = "#{user.name} <#{user.email}>" is better than email_with_name = user.name + ' <' + user.email + '>' is that the latter creates 3 strings and the former just 1.

That's a wrap

In the next installment I will discuss the next 10 mistakes I often make.

Permalink — Comments or kudos? Find me on Twitter.

Are you a project or product manager? Ship better software with Releasewise!