Stop Fighting The Last War

There is a classic military problem at the start of a new campaign. The goals and missions initially attempted are based upon the doctorine that worked in the previous campaign. This can have disastrous effects. During the first Gulf War this resulted in a convoy of tanks left inoperable at the side of the road. A key filter needed to be replaced to allow use in a desert.

I am shortly to start on a new project at a new client in a very different domain to any that I have worked in before. This will involve checking all of the assumptions that I am about to make.

The previous engagement had poorly defined goals. This is one point that can be corrected early on for the next project. What is the problem we are trying to solve? How do we measure progress towards this goal? What are the constraints here?

Rate Limiting Lambdas

Recently I managed to accidentally exhaust the pool of concurrent lambda executions on an account.

We have an S3 bucket full of files that we need to parse and store in a database. They get parsed by a trigger when added. We have added some code that can extract extra details from the files. The naive approach is to have a scheduler trigger the Lambdas at a fixed rate. The problem was that due to another problem the database became overloaded. This led to a sorcerer’s apprentice style of retry overload.

We now use reserved concurrency combined with an SQS queue to limit how many messages are processed at a time.

Reserved concurrency takes some of the concurrent lambda capacity (which defaults to 1000 per account) and restricts a given lambda to use that. I would prefer the ability to have a cap rather than dedicated capacity, but I can see why it is there.

I have taken advantage of the long weekend to tune the values of timeout and reserved capacity so that I don’t overload my Postgres database. Thread pooling is hard when you have Lambdas.

Lesson learned:

– Lambdas are not an unlimited resource.

– Don’t have too many unrelated services in the same account

– SQS plus Lambda with a dispatching lambda allows effective rate limiting.

Software Design X-Rays

I have started studying this book.

To make life easier here is the link to the examples:

http://www.adamtornhill.com/code/xrayexercises.html

So far I agree with the analysis. One minor niggle is the claim that small config files don’t need tests. That’s not true for say package.json.

My dependabot usage results in dependabot making 60 times as many commits as the next user. You really need a unit test for every feature that you use in a dependency. In the node world that can be painful.

First Looks at Tailwind CSS

I am working through the book Modern CSS with Tailwind.

Tailwind is a css framework for styling a website.
It is strongly opinionated on certain things.
To start with it includes a complete reset library that removes all browser default styling for all HTML elements.
The markup is effect describing rather than semantic:

text-gray-300 hover:text-gray-700

This is intended to make the markup very easy to understand, at the price of being verbose.

Tailwind CSS 2.0 has taken the decision not to support Internet Explorer 11. This will allow a lot of modern (or technically by now non-ancient) CSS techniques to be used. This would prevent me from using it on my current client who still has a 1/6 of the users reporting as ie 11.

Microsoft have made some announcements about their support for IE11: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666

Microsoft teams has already stopped supporting IE11 as of November 2020.
Microsoft 365 (the current name for the online office suite) will drop IE11 support in August 2021.
It will be interesting to see how quickly the big corporates drop IE11 support.

Concurrent Data Processing In Elixir (B2)

This book works you through the various concurrent programming techniques available to Elixir.

This is still in Beta, so not yet finished (and has the last few typos to be fixed), but is a great introduction.
It even covers some of the recently added features of the venerable GenServer so you get to see how to use handle_continue which is great technique that allows a genserver to return and still carry on working on a problem.

It starts with Tasks, introduces GenServers.
Then it moves onto GenStage (a system for using Backpressure to control the flow of data through an application.
Flow adds wrappers around GenStage to simplify the usage (it’s almost as easy as Streams).

The examples are mostly contained within individual chapters which makes picking up a topic much easier than other PragProg books that I have worked through.

Awaiting the B3 release!

Heisenberg And Death March Projects

A simple explanation of the Heisenberg Uncertainty Principle goes as follows:

You have a particle that you are trying to find out where it is an how fast it is moving. You are taking measurements by shining a laser onto it. If you want more accuracy on the location then you can shine a brighter laser. However this changes the velocity. If you want a more precise velocity then you need to use a dimmer laser. There is a finite limit to the combined accuracy or position and velocity.

There is a similar problem with projects that are deemed late. Frequently the solution is to add more frequent and intensive status reports. The problem is the addition of the status report takes away time that could be spent working so it will actually delay the result. In extreme cases the project will then never be finished as all time is taken up by reporting.

Learning Go

Later this week I am going to be assessing a developer on a Pair Programming TDD Programing Exercise in Go.
The problem is that I currently don’t know Go.

I do own the book Introducing Go from a Humble Bundle.

These are the notes that I have from working through the first few chapters.
I am basing the analysis upon the 7 Languages in 7 Weeks pattern.

Go is strongly typed.

Bracket style is K + R (opening bracket stays with the function declaration)

Integer size is machine (platform dependent)
Method names seem to use PascalCase.
Variable names use camelCase.

Variables can only be defined once per scope.
Variables are mutable.

Declared by var
The type follows the name if required:

var x string

I have not seen this in recent languages (this is a Pascal idea rather that the C style of type name)
Types can be implied by assigning with := (but only inside a function).

const allows the creation of things that cannot be reassigned.

Backticks allow for multiline strings.

Compiler warns about unused variables.

Variable names can be redefined at a different scope.

No implicit returns from functions. (Which is unusual for a modern language).
You can name the return variable, assign it and then call return.

You can return tuples from a function.

Does not handle overflows well.

func factorial(x uint) uint {  
    if x == 0 {  
       return 1  
    }  
    return x * factorial(x-1)
}

This fails on 100, and will return 0

It appears that a function cannot be overloaded.

Interfaces are implicit. You don’t need to state that you are using it.

The early versions of Go were really weak at package management. It requires you to have your code in a sub folder of the GOPATH called src. This completely insane! The language should not define the name of the folder structure.

Tests need to be named Test* and have a parameter of t *testing.T

I think I now know enough for the Pairing Interview.

Seven Databases in Seven Weeks Part 1

The project that I am working on finally got the requirements that allowed us to pick a database. Over the last few years I have been using MongoDb as the lazy choice for storage. The project makes more sense to use a relational database so I have returned to Postgres.

This has encouraged me to reread this book. I am planning on adding some details in these notes to add modern practices to the book.

To start with the book predates containers so all of the chapters require you to install the database on your machine. I am not encouraging you to use a database in a container for production. It does however allow you to test drive database code.

Over the last decade all of the big databases I have used have been cloud hosted. For a JVM based application we used hsqldb as an embedded replacement for the database in tests.

The book also fails to mention database migrations. This is a technique that allows you to version control the schema of a database.

Coming back to Postgres after years of Mongo makes me realize that you need to work a little harder to get data to and from the store. On the other hand SQL is far easier to make aggregate queries with compared to Postgres. It is easier to enforce constraints in SQL. Types, unique indexes, non-null fields and foreign keys all help.

The book also only works at the database level. I want to add examples of how to connect to the database.

The Trouble With Terraform

Recently I have been working with Terraform to stand up the infrastructure for the project that I am working on. The project involves a database plus a number of lambda functions. We have a CD build pipeline and use Terraform for the infrastructure, run from cdflow.

Terraform is great for defining the infrastructure. The problem comes when you rename something (module names in particular). Terraform will attempt to tear down the service and recreate it. Sometimes AWS services are eventually consistent. This can mean that a deleted resource hangs around for a while after being deleted. A rename of a module will delete and recreate the item, which will frequently fail on the first pass.

You also need to be very careful that you only build to a given environment from a single branch of the build pipeline. Not doing so allows databases to be torn down. I have seen an incident where a developer comments out infra that is not needed for the current build, only for that change to delete the production database and all the backups. There are things that can be done to prevent this (marking backups as requiring an extra switch to be removed). These changes then leave Terraform unable to completely clean up.

I am not arguing against configuration as code, merely noting that you will get a lot of failed builds. Some of these are resolved by rerunning the job/pipeline. Others require the resource to be manually deleted.

Terraform operates at a level of abstraction that can both help and hinder. Some of the abstractions are a little weak (these will improve in time) especially when defining IAM permissions as you end up with inline strings that explicitly define the policy.

Why Dependabot Works

This is the theory of why dependabot works so well.

With atomic changes you know what triggered the break (or find that your tests are unreliable, which is also valuable).