Today, I am going to share with you the most important lessons I have learned by building Release Butler — a Twitter bot that tweets beautifully styled changelogs of popular frontend frameworks and libraries like Angular, React, Vue or Webpack…

Example of a tweet made by Release Butler

Release Butler got very positive welcome from the community with lots of engagement and more than 600 followers during the first month after it’s release in the beginning of April 2018.

Release Butler got pretty good traction during the first month after its release!

The lessons (tldr)

⌛ Don’t try to save time by skipping Typescript 🏋 Async /await is great until it isn’t & exception handling can be tough 🔑 Logging is the key 🎉 Going vanilla on the frontend without a framework can be refreshing 🤗 Embrace the CLI and build your own tools 🏛️ “Now” is a great platform for building bots — node vs Docker 🥊 Not all APIs were created equal — GitHub vs Twitter

🛡️ BONUS: Basic security

The context

I am currently traveling around the world for the ninth month. Believe it or not adventure can become repetitive too… You can easily find yourself yearning for “something” to do…

I am very happy to be part of the wonderful tech industry and love the idea of giving back to the community by trying to create something useful. Lately, I have been very inspired by amazing carbon.now.sh project which enables you to create and share beautiful code snippets.

Release Butler lives at releasebutler.now.sh

This led to an idea to create similar service, but for the changelogs instead. The concept later cristalyzed in what is now called Release Butler.

Besides being a Twitter bot, it comes also with a website which enables you to download changelog of any GitHub hosted library which uses GitHub releases or Changelog.md file.

Follow Release Butler, a twitter bot that helps you to stay up to date with releases of popular frontend libraries…

⌛ 1. Don’t try to save time by skipping Typescript

The origins of Release Butler can be described more like a disorganized experimentation than a well-thought project with detailed architecture, milestones and what-not…

In the beginning, a single small moment of laziness resulted in skipping of Typescript which has now major consequences…

Let me just tell you, use Typescript, it’s worth it!

The effort to start using Typescript in a node project is really low. All we need to do is run npm i -S typescript ts-node and then execute our application using ts-node . instead node . . We don’t even have to type every variable or function from the start. Types can be added gradually, every time one of the interfaces becomes more stable.

While some of the pain can be prevented by a more detailed architecture docs before the start of the project, it is NOT reasonable to expect that project will be designed perfectly from the beginning.

Requirements tend to evolve and we often need to reorganize our code base. Code reuse opportunities and abstractions become more apparent as we add more services and components. Refactoring can become a major pain even in a small project like Release Butler.

Every time we have to change interface of any of our services without a strong type system we are at risk of breaking some distant part of our application

B ut what about the tests? This wouldn’t be a concern if we had a proper test coverage, right?

The main disadvantage of tests to solve this kind of problems is that we have to write them in the first place…

I am a huge fan of writing unit and integration tests for the business logic

On the other hand, seeing lots code that checks for the presence or typeof of a function arguments and corresponding unit tests is a big code smell… 💩

Typescript can dramatically reduce amount of code needed to guarantee basic correctness of the application. Besides that, most of the popular editors come with substantial refactoring capabilities when used together with a typed language like Typescript.

🏋 2. Async / await is great until it isn’t & exception handling can be tough

Async / await is a newish addition to the JavaScript language. I haven’t really used it before and worked directly with the promises instead…

Simplified example of using async / await in Release Butler codebase

The main premise of async / await is to make async code look just like plain old sync stuff. Every line of code executes in the top to bottom fashion while waiting (await) for the result of asynchronous operation when necessary.

It’s also great for more complex orchestration of multiple async services resulting in much “flatter” code compared to callbacks or promises.

The biggest challenge of using async / await comes with the implementation of exception handling

Attaching .catch(err => { /* handle error /*}) handler at the end of the local promise chain is much more readable than wrapping different and often nested parts of the execution in try / catch blocks.

As the project grows, the number of async functions becomes larger and orchestration more complex. It is no longer enough to just wrap every single async execution with a try / catch block and handle all exceptions locally.

A lot of consideration has to go into figuring out how to handle unexpected behavior with respect to the overall desirable functionality. We tend to end up with following cases:

exception should be handled at the current level and result in valid state exception should be handled partially at the current level (for example we log error) and re-thrown for handling up in the execution stack exception should be not handled at all at the current level and will be caught by some of the parent functions

This can get tricky. Async functionality offered by one of the services can be consumed by different parts of application with wildly different expectations about what will happen when things go wrong…

🔑 3. Logging is the key

In frontend development, we’re used to getting immediate visual feedback for every change of code we just authored. On the other hand, long running backend process doesn’t provide any useful information out of the box. By default, we know only if it runs, have stopped or crashed…

Logging is THE solution for getting useful insight into current state of our backend system and the easiest thing to do is to start logging everything

Incomming API request? Database query or script execution? No problem, logs got us covered…

But as with everything, it is important to strike a nice balance — too much and we will get drowned in the noise, too little and we’re running blind in the darkness of the night…

Iteration on what and how to log is a ongoing process…

Current logs for a single Release Butler execution provide quick overview of the released projects and their versions

Current logs are very concise. A lot of information was removed over the course of iteration…

timestamp — now platform provides its own timestamp

No new version for project: <project-name> — as it turned out, it is much more interesting to know what has happened instead of what didn’t happen

— as it turned out, it is much more interesting to know what has happened instead of what didn’t happen names of tracked projects — five tracked projects can fit into one line but the number soon increased to more than 20, that’s a lot of useless repetion

list of all released versions — it is nice to know what was released since last deployment but it can get problematic if bot runs for months and the list grows to hundreds of versions, the compromise was to log number of releases and the last version

various implementation related info — details of every request or database query can be useful but adds too much noise during normal operation so it is better to use debug log level which has to be enabled manually when needed

🎉 4. Going vanilla on the frontend without ANY framework can be refreshing

Inspiration for Release Butler came partially from a great project called carbon.now.sh. It’s website which enables creation of beautifully looking code snippets. Release Butler strives to enable you to get and share beautifully looking changelogs instead.