Learn about the technology, culture, and processes we use to build Hootsuite.

Everyone — our software developers, co-op students, high school students — 'works out loud' about the tools we use, the experiments we ran, and lessons we learned. We hope our stories and lessons help you.

Recent Posts:

For our continuous delivery practice, an important part of shipping code to production is running automated tests against those production candidates to ensure the version of those applications work as we expect. Sometimes those automated tests take so long to run that it feels like it kills the flow of continually shipping. So, one of our goals this summer was to reduce testing time, even when the number of tests is high, so we can keep pushing code to production. By introducing Bazel to our Insights product we were able to reduce the testing time by 50% from 7 minutes to 3.5 minutes, and reduced our time-to-deploy by 70% from 9 minutes to 2.7 minutes.

Bazel

Bazel is the open-sourced cousin of Google’s internal tool – Blaze. Bazel allows the automation of building and testing, developed by Google too. What makes Blaze unique is that it has been designed to solve build problems specific to Google’s development environment. According to Jeff Cox from Google’s Bazel team, it is suitable for projects that involve massive, shared code repositories, extensive testing and release processes and language and platform diversity.

It has proven to be both scalable and reliable after extensive usage by tens of thousands of engineers and tested over years on Google’s environment. It has proven to be build software quickly and correctly too. It’s speed is achieved by caching and parallel execution, as Bazel rebuilds only the files that are needed, instead of the entire project and runs tests only for the code that has been changed.

To understand Bazel and see if we could benefit from it, we first had to understand how it worked. Bazel works with WORKSPACES and each workspace is defined by a file named WORKSPACE (which can be empty). Inside every workspace you can define BUILD files with rules and each defined rule is a target. Each target will be a node in the dependency graph, and the target’s dependencies are edges.

bazel-workspace-and-targets
WORKSPACE – defines the project workspace (folders hierarchy). BUILD files – define targets using rules

Bazel queries

Rules describe the dependencies between files and packages. If you want to get an analysis of these dependencies, you can use the query language offered by Bazel. Bazel’s query language is a language of expressions which operate over the build dependency graph, which is the graph implicitly defined by all rule declarations in all BUILD files. You can find the dependencies of a rule, trace the dependency chain between two packages or select the set of targets that depend on some target by executing a Bazel query.

For example, if you change a library and you want to find what are the dependent libraries, you can run the following query: bazel query ‘kind(library, rdeps(//…, //database/mongodb:mongodb))’, and you can also visualize the graph of your dependencies”

bazel-queries

Tests

Bazel offers possibilities for testing and one of them is writing build rules that define automatic tests which can be run with ‘bazel test … ’ command. The tests runner can work on multiple test targets defined by rules. Bazel provides an parallelization system which allows multiple jobs at the same time to be executed. The system can used by specifying –jobs argument to ‘bazel test’ command. Another built-in system which comes with Bazel is it’s Caching System. It caches all PASSED tests, and when the same tests are executed, if no change was made to files or dependencies, they will be skipped. This is the main reason we used Bazel in our project and we have an improvement of 50% on the time spent on testing from 5 minutes to 2.5 minutes.

Another strong feature of testing in Bazel is specifying a size or time for each test. By using it you can establish for tests time limit, and if tests took more than that, it will automatically fail. More options for testing can be found on official tests documentation.

Extending rules

Bazel offers flexibility on rules and allows you to write your own rules using Skylark Language and import them in BUILD files, and all targets defined with a custom rule will be executed with custom rule’s code. We use this possibility to write our custom rule of py_test, because the default rule is using pytest as tests runner, and we needed nosetests.

This extension is possible because every rule is converted in a bash script which is executed inside inner tree of symlinks.

Results

After integrated a big part of project with Bazel we have obtained next results:

In 34.5% of cases from a metric of 500 tests, only 100 to 200 tests are executed. This means almost one third of total tests are executed so testing time can decrease by more than half (depending on execution time and dependencies). There are many chances for a test to be executed if it has a big number of dependencies.

So, when we hit the deploy button, only 23 % of tests are executed and total time spent on deploy was reduced from 9 minutes to 2.7 minutes, and a considerable part of the time is deploy’s overhead, which it cannot be diminished with Bazel.

Conclusion

Given that Bazel is developed and supported by Google with the stated goal of productive development environment, where code can be shipped as fast and bug-free as possible, we can trust the reliability and flexibility of this build system.

If your project has a large shared codebase that supports multiple platform, written in multiple languages which has a huge number of tests, and if you desire a full overview of base code, Bazel might be a good choice as a build system for it.

About the Authors

rodica-davidteodor

Teodor and Rodica are students in 4th year at University of Politehnica Bucharest at Faculty of Automatic Control and Computer Science where we found our passion for technology and innovation. We’ve been lucky to work this summer with Hootsuite team from Bucharest, as interns and to help them to improve testing time on deploys. Beside technology, Teodor loves basketball and to travel as much as he can, meanwhile Rodica likes to read novels and finds a passion in everything she does. But now, as the summer has ended, it’s time for us to get back to school and start working on our final projects for bachelor’s degrees.

Tagging has become a ubiquitous part of modern web applications ever since it was popularized by Flickr and Delicious in the early 2000s. The ability to add a tag to arbitrary content allows a user to organize, filter, manage, and measure however they see fit. This flexibility of application makes tagging a very valuable feature to invest in.

tag: rawr
tag: rawr

Tagging has been a function of the Hootsuite dashboard for some time. In its current incarnation, users have the ability to add tags to messages in their streams, and to add tags to their outbound messages. This allows them to generate analytics reports on things such as the number of retweets of their messages with a given tag, or how often they receive messages with the tag ‘Pokemon Go’. In addition, organizations have the ability to tag arbitrary uploaded content, such as files or images, so that a team member can more easily find resources for composing messages.

Read More …

The Dark Side of Modularization

Working on our iOS application has been an incredibly eye-opening experience. I was anticipating being overwhelmed by jumping into a colossal monolithic application with a complicated set of interdependencies. Luckily for me, our mobile team was on the same track as our web teams and had taken steps to avoid a monolithic architecture for both their iOS and Android applications. The decision was made to fragment the application into highly cohesive modules that abstract away implementation details from the main application. On iOS, externalizing code is best done with frameworks; which are libraries that can also contain resources like xib files, storyboards and images. At the beginning of my work term, our app was consuming over a dozen internal frameworks and several external frameworks. As a result of managing so many dependencies, the time it took to build our app had skyrocketed and became a serious block in our daily workflow. Just imagine having to wait 25 minutes every time you wanted to get the latest changes from the repository!

A New Hope

Over the last few months we have made several key revisions to our workflow that has allowed our framework approach to be far more effective, and ultimately, made our lives easier.

Our transformation into frameworks began with extracting the small, reusable, components and has now extended to features as large as the entire searching tab of the application. What makes this framework approach so valuable stems from the roots of modularization as a software technique: maintainability, reusability, and abstraction. With each major feature of our application in it’s own framework, different iOS teams at Hootsuite are able to maintain different frameworks independently, each with their own separate commit history. Many of our internal frameworks depend upon each other, they were designed to be as reusable as possible. Each framework has a well defined public interface, the implementation details of a framework are inherently abstract. As a co-op starting out in a new position, this level of abstraction and modularity has made my life easier and far more productive!

We have categorized our frameworks as either a Component, a Feature, or a Core utility. This graph doesn’t include all of our frameworks, but it illustrates the hierarchy:

iOS Features Components Core

When is it suitable to make a framework?

Read More …

On the Hootsuite Android team, we recently found ourselves looking for a library that implemented material chips. Unfortunately, most of the libraries that existed were extremely tightly coupled to providing chips for contacts, and we didn’t need any of the contact-related features. All we needed were chips that contained text and an optional icon, and a TextView that could provide suggestions for the user as they were typing. Unable to find an existing solution, we decided to write our own library and open-source it: Welcome to Nachos.

A terrible (but relevant) joke.
A terrible (but relevant) joke.

Read More …

How our Lack of SQL Joins and Foreign Keys Aided Us in Our Journey

“We don’t have foreign keys in our databases at Hootsuite.”

“What?!”

My friend practically choked on his coffee. I gently patted him on his back as he bent over and coughed, his eyes nearly popping out of his head. I felt a strange surge of excitement at his reaction, much like the kind of excitement one gets upon telling a child Santa Claus is not real. Not that I would ever do that, of course.

“We also don’t do any SQL joins,” I said.

“What?!” he spat, “Okay sure fine yeah okay but why would you not want to enforce correct data?! This is so absurd!”

He gripped his head and looked at me, fear and confusion in his eyes (and a bit of coffee on his shirt, unfortunately). After believing something for so many years, how could anyone come along and say anything otherwise? How dare anyone deny the truth? Unless, maybe.. The truth has been a lie all along?

“Are you going to tell me why?”

“Nah, you’re gonna have to wait for my blog post.”

Read More …

Loading ...