We spent a week making Trello boards load extremely fast. Here’s how we did it.

January 22nd, 2014 by Bobby Grace

We made a promise with Trello: you can see your entire project in a single glance. That means we can show you all of your cards so you can easily see things like who is doing what, where a task is in the process, and so forth, just by scrolling.

You all make lots of cards. But when the site went to load all of your hundreds and thousands of cards at once, boards were loading pretty slow. Okay, not just pretty slow, painfully slow. If you had a thousand or so cards, it would take seven to eight seconds to completely render. In that time, the browser was totally locked up. You couldn’t click anything. You couldn’t scroll. You just had to sit there.

With the big redesign, one of our goals was to make switching boards really easy. We like to think that we achieved that goal. But when the browser locked up every time you switched boards, it was an awfully slow experience. Who cared if the experience was easy? We had to make it fast.

So I set out on a mission: using a 906 card board on a 1400×1000 pixel window, I wanted to improve board rendering performance by 10% every day for a week. It was bold. It was crazy. Somebody might have said it was impossible. But I proved that theoretical person wrong. We more than achieved that goal. We got perceived rendering time for our big board down to one second.

Naturally, I kept track of my daily progress and implementation details in Trello. Here’s the log.

Monday (7.2 seconds down to 6.7 seconds. 7% reduction.)

Heavy styles like borders, shadows, and gradients can really slow down a browser. So the first thing we tried was removing things like borders on avatars, card borders, backgrounds and borders on card badges, shadows on lists, and the like. It made a big impact, especially for scrolling. We didn’t set out for a flat design. Our primary objective was to make things faster, but the result was a cleaner, simpler look.

Tuesday (6.7 seconds down to 5.9 seconds. 12% reduction.)

On the client, we use backbone.js to structure our app. With backbone, it’s really convenient to use views. Really, very convenient. For every card, we gave each member its own view. When you clicked on a member on a card, it came up with a mini-profile and a menu with an option to remove them from the card. All those extra views generated a lot of useless crap for the browser and used up a bunch of time.

So instead of using views for members, we now just render the avatars and use a generic click handler that looks for a data-idmem attribute on the element. That’s used to look up the member model to generate the menu view, but only when it’s needed. That made a difference.

I also gutted more CSS.

Wednesday (5.9 seconds… to 5.9 seconds. 0% reduction.)

I tried using the browser’s native innerHtml and getElementByClassName API methods instead of jQuery’s html and append. I thought native APIs might be easier for the browser to optimize and what I read confirmed that. But for whatever reason, it didn’t make much of a difference for Trello.

The rest of the day was a waste. I didn’t make much progress.

Thursday (5.9 seconds down to 960ms)

Thursday was a breakthrough. I tried two major things: preventing layout thrashing and progressive rendering. They both made a huge difference.

Preventing layout thrashing

First, layout thrashing. The browser does two major things when rendering HTML: layouts, which are calculations to determine the dimensions and position of the element, and paints, which make the pixels show up in the right spot with the correct color. Basically. We cut out some of the paints when we removed the heavy styles. There were fewer borders, backgrounds, and other pixels that the browser had to deal with. But we still had an issue with layouts.

Rendering a single card used to work like this. The card basics like the white card frame and card name were inserted into the DOM. Then we inserted the labels, then the members, then the badges, and so on. We did it this way because of another Trello promise: real-time updates. We needed a way to atomically render a section of a card when something changed. For example, when a member was added it triggered the cardView.renderMembers method so that it only rendered the members and didn’t need to re-render the whole card and cause an annoying flash.

Instead of building all the HTML upfront, inserting it into the DOM and triggering a layout just once; we built some HTML, inserted it into the DOM, triggered a layout, built more HTML, inserted it into the DOM, triggered a layout, built more HTML, and so on. Multiple insertions for each card. Times a thousand. That’s a lot of layouts. Now we render those sections before inserting the card into the DOM, which prevents a bunch of layouts and speeds things up.

In the old way, the card view render function looked something like this…

render: ->
  data = model.toJSON()

  @$.innerHTML = templates.fill(
  ) # add stuff to the DOM, layout

  @renderMembers() # add more stuff to the DOM, layout
  @renderLabels() # add even more stuff to the DOM, layout


With the change, the render function looks something like this…

render: ->
  data = model.toJSON()
  data.memberData = []

  for member in members
    memberData.push member.toJSON()

  data.labelData = []
  for labels in labels when label.isActive
    labelData.push label

  partials = 
    "member": templates.member
    "label": templates.label

  @$.innerHTML = templates.fill(
  ) # only add stuff to the DOM once, only one layout


We had more layout problems, though. In the past, the width of the list would adjust to your screen size. So if you had three lists, it would try to fill up as much as the screen as possible. It was a subtle effect. The problem was that when the adjustment happened, the layout of every list and every card would need to be changed, causing major layout thrashing. And it triggered often: when you toggled the sidebar, added a list, resized the window, or whatnot. We tried having lists be a fixed width so we didn’t have to do all the calculations and layouts. It worked well so we kept it. You don’t get the adjustments, but it was a trade-off we were willing to make.

Progressive rendering

Even with all the progress, the browser was still locking up for five seconds. That was unacceptable, even though I technically reached my goal. According to Chrome DevTools’ Timeline, most of the time was being spent in scripts. Trello developer Brett Kiefer had fixed a previous UI lockup by deferring the initialization of jQuery UI droppables until after the board had been painted using the queue method in the async library. In that case, “click … long task … paint” became ”click … paint … long task“.

I wondered if a similar technique could be used for rendering cards progressively. Instead of spending all of the browser’s time generating one huge amount of DOM to insert, we could generate a small amount of DOM, insert it, generate another small amount, insert it, and so forth, so that the browser could free up the UI thread, paint something quickly, and prevent locking up. This really did the trick. Perceived rendering went down to 960ms on my 1,000 card board.

That looks something like this…

Progressive Rendering

Here’s how the code works. Cards in a list are contained in a backbone collection. That collection has its own view. The card collection view render method with the queueing technique looks like this, roughly…

render: ->

   renderQueue = new async.queue (models, next) =>
     @appendSubviews(@subview(CardView, model) for model in models)
     # _.defer, a.k.a. setTimeout(fn, 0), will yield the UI thread 
     # so the browser can paint.
     _.defer next
   , 1

   chunkSize = 30
   models = @getModels()
   modelChunks = []
   while models.length > 0
     modelChunks.push(models.splice(0, chunkSize))

   for models in modelChunks
     # async.queue flattens arrays so lets wrap this array 
     # so it’s an array on the other end...
     renderQueue.push [models]


We could probably just do a for loop with a setTimeout 0 and get the same effect since we know the size of the array. But it worked, so I was happy. There is still some slowness as the cards finish rendering on really big boards, but compared to total browser lock-up, we’ll accept that trade-off.

Trello developer Daniel LeCheminant chipped in by queueing event delegation on cards. Every card has a certain number of events for clicking, dragging, and so forth. It’s more stuff we can put off until later.

We also used the translateZ: 0 hack for a bit of gain. With covers, stickers, and member avatars, cards can have a lot of images. In your CSS, if you apply translateZ: 0 to the image element, you trick the browser into using the GPU to paint it. That frees up the CPU to do one of the many other things it needs to do. This browser behavior could change any day which makes it a hack, but hey, it worked.


I made a lot of bugs that week, so I fixed them on Friday.

That was the whole week. If rendering on your web client is slow, look for excessive paints and layouts. I highly recommend using Chrome DevTool’s Timeline to help you find trouble areas. If you’re in a situation where you need to render a lot of things at once, look into async.queue or some other progressive rendering.

Now that we have starred boards and fast board switching and rendering, it’s easier than ever to using multiple boards for your project. We wrote “Using Multiple Boards for a Super-Flexible Workflow” on the Trello blog to show you how to do it. On the UserVoice blog, there’s a great article about how they structure their workflow into different boards. Check those out.

If you’ve got questions, I’ll try and answer them on Twitter. Go try out the the latest updates on trello.com. It’s faster, easier, and more beautiful than ever.

Bee: A Desktop FogBugz Client for Mac

November 20th, 2013 by Adam Wishneusky

Bee is a Mac client for FogBugz. It makes tracking your cases simpler and faster with its beautiful design with fluid animations. It is deeply integrated with Mac OS X so you can receive notifications from your teammates and use QuickLook to view case attachments. You can time cases from your menubar and Bee also intelligently tells you what you should be working on next. In addition to FogBugz, Bee also works with plain text files for your project notes and supports syncing tasks with GitHub and JIRA.

Bee requires Mac OS X 10.8+ and is available from the Mac App Store. A free trial and further information is available from the Neat.io site.

Fog Creek is Hiring, and We’re Coming For You!

November 14th, 2013 by Rich Armstrong

Boy are we hiring!

There are currently five different job postings up on our Careers page, and in some of these roles, we’re looking for multiple people.

So, that’s nothing new. But the big news that you may have missed is that, for almost all roles, we’re looking for candidates all over the world, not just in New York city.

That’s right! Fog Creek is coming to you!… Virtually! (No, we will not be literally bringing our espresso machine to you.)

We’re not just looking for drones here. Working remotely is not an impediment to career growth at Fog Creek. Productive teams keep remote employees in mind from the start. We rely heavily on asynchronous communication through products like Trello, and we conduct our (few) meetings online.

Remote developers are equal team members in every way. We even have remote team leads, who manage their teams from afar… well, from various distances, really.

Whether you want to work remotely, or to enjoy our beautiful offices and hanging out with our awesome employees, you’ll find you have a real career here at Fog Creek.

So, you no longer have to move to New York just to get reasonable work hours and awesome colleagues in an ideals-driven culture. You no longer have an excuse. Apply today!

Trello Server Team on NodeUp Podcast

November 12th, 2013 by Brett Kiefer

The Trello server team appears on this week’s NodeUp podcast, talking about the Trello tech stack in production.

Taco Headphones


Case Event Merging – One Case to Rule Them All

November 8th, 2013 by Stephen Asbury

If you’ve been using FogBugz for issue tracking or customer support for a while, you’ve probably encountered a situation like this. One of your customers sends you an email asking for a new feature, and FogBugz creates a case for you to track the request.


Five minutes later, the same customer sends another email with brilliant new insights as to how the feature should work. Here comes another case.


So now you have two cases about the same request. What do you do? You don’t want to reply to the same customer request in two places. You don’t want to copy and paste information from one into the other. You or someone else at your company might miss the duplicates list and the other email chain. It would be much better if you could see both emails in a single case.

Now you can!

Introducing Case Event Merging

Well, maybe I should say re-introducing. We introduced case merging back in August of 2012. At that time we received an excellent solution from one of our customers, for which we are very grateful. We liked the idea so much that we have built it into the FogBugz On Demand Performance Upgrade, hopefully with a few improvements.

Now, when a case is marked as a duplicate, FogBugz does two things. First, it places a message at the top of the duplicate case telling you that its events are now also merged into another case.


Second, FogBugz provides you with controls on the main case, located in the “Duplicates” area of the side bar, to show or hide the merged events.


Three controls indicate the types of events you can merge from duplicate cases. From left to right these are:

Email events are shown by default so that you can see the full conversation with your customer. Comments and BugzScouts are initially hidden. Click on each control to toggle the visibility of the merged events. The events are inserted into the normal event list and highlighted in purple, as shown below. They will also let you know which case the event comes from.


We have found that most times we want to see events from a duplicate case, it’s the email events we care about. Sometimes you do need the comments, so you can click the control to show them. When looking at crash report cases created by BugzScout, there are often so many events that showing them all can be overwhelming, so they’re hidden by default. Click the control to show them if you need to see every report of the error.

No more clicking around to get all of the information you need! Case event merge consolidates information from multiple cases, allowing you to see everything you need – in one case.  If you’d like to use native Case Event Merge, sign up for the Performance Upgrade to FogBugz On Demand (if you haven’t already). Get started by sending us your site URL.

Update: We have improved case merging! See the docs for details.

Responsive HTML Emails: a Different Strategy

November 1st, 2013 by Tina Ye

Email has become quite the funny thing. It follows us everywhere. To the grocery line. To the dinner table. To the… shower? (Well… why not.) So when we set out to rethink the FogBugz email notifications from the ground up, we knew we had to ensure a great in-shower email experience—and that meant optimizing for mobile.

There are a wealth of resources on this topic. Campaign Monitor has an  exhaustive, start-to-finish guide. Smashing Magazine posted about various mobile email design tricks. MailChimp has written definitively on the topic. And if you don’t want to read so many words, Litmus recently provided a juicy summary of their own.

However, there is one small problem with all of the above: they all advocate the use of media queries.

A Media Query Quandary

Media query support can pretty much be  taken for granted in desktop browsers, but it remains unknown to many email clients today. One of the most popular mobile email clients is the Gmail app for Android and iPhone, and it remains a staunch non-attendee of the media query party. Without media queries to detect device size and reconfigure the layout accordingly, users could be stuck squinting at something like this:


Pinch me!

A mobile design strategy that doesn’t address one of the most popular mobile email clients isn’t a very good strategy. So we thought… what if we took a different approach?

One Layout to Rule Them All

Most mobile email design articles still want you to think “multiple layouts for multiple screen sizes.” For instance, design a two-column layout for desktop, and then adapt it into a single-column layout for mobile. This strategy relies heavily on media queries to detect the device width and then activate the appropriate layout by reflowing content, shrinking headers, or hiding entire sections. You can see why this would be a problem when media query support is still so spotty…

So rather than depend on media queries to modify the layout, we thought, “Why not just design one layout that looks good on all screen sizes?”

Impossible? Nay, it’s possible!

Mobile First

I was inspired by Luke Wroblewski’s  mobile first approach. Here, one considers the use case with the most limitations first (mobile), and then designs upwards from there. After all, it is easier to fit a small kitten into a big jar than a big kitten into a small jar (They were having a sale at the metaphor factory, okay?). Aaaanyway, it’s a great design philosophy, and it made all the difference in how we arrived at our final design.

We began by asking, what layouts work well on mobile? The answer for us was to think “single column.”

Screen Shot 2013-10-14 at 4.03.36 PM-1

By single column, I mean that the primary flow of the content is downward rather than both downward and side-by-side. Side-by-side content usually requires reflowing to fit on a smaller screen. In contrast, a linear downward flow means content simply scales horizontally to fit the available space. Voila, one layout for any screen size!

Screen Shot 2013-10-15 at 1.43.06 PM

That said, there are times when side-by-side content will not need to be reflowed to fit on smaller screens. Sometimes, the contents of a column can work just as nicely when narrowed down on a smaller screen. Of course, I would not advocate compressing paragraphs of text down to 160px-wide corridors, but it is okay to maintain the side-by-side position of things like labels, asides, notes, and icons. You’ll see lots of examples of this in the final FogBugz notification email design.

Finally, lest you think a single-column approach is too aesthetically restrictive, here are a couple of very nice-looking designs using a predominantly single-column approach. Check out this lushly illustrated piece from Code School. Or this beautifully typeset promo email from Tinkering Monkey.*


These are the “desktop versions” of the emails, but because they are single-column, you can easily imagine how they’d scale down to mobile. Screenshots courtesy of Email Wizardry.

As for the design of the Fogbugz notification emails, our goal is to communicate the status of a case quickly and effortlessly. We don’t need a whole lot of stylized typography or eye-catching illustration to do this—that would be distracting and counter-productive. On the other hand, we do need to convey a lot of information, so we relied on strong typographical hierarchy, a flexible grid, and subtle color to organize the design. Here’s a peek at the final design:


Because of the diversity of information present, it’s quite a bit more complex than a strictly single-column layout. But it acts like one: it scales nicely horizontally, and does not need to be re-stacked or rearranged to fit on a smaller screen. One layout, multiple screens.

Now that I’ve hopefully convinced you to free yourself from the shackles of designing multiple layouts, let’s see what this looks like at the implementation level…

Writing the Codez

On mobile, you have limited real estate, so it makes sense to use as much of the screen as possible. A fluid width: 100% layout does the trick. We simply apply that inline to the largest container element, and get something like this:


But on desktop, this same code would create a readability nightmare:


This is easy to fix. Let’s put in some breathing room on the sides, with a simple little max-width added (also inline) to the main container element:


Ah… much better. Now we’re not violating any typographic rules about sane line lengths.

And that’s basically it! You now have your completely responsive, mobile-and-desktop-friendly, superhero-quality email layout, all without using a single media query.

And you thought this section was going to be long.

Wait, What About Outlook?

Darn, we can’t go party just yet! As it turns out, even an old CSS2 property like max-width isn’t recognized in all email clients. Without it, our emails will just go back to being super-wide and hard-to-read. What can we do to solve this?

If you can say with confidence that none of your users will ever view an important email in Lotus Notes, Outlook, or even Apple Mail, then you can skip all this nonsense and head down to the club. For the rest of us, we’ll catch up with you later. It won’t take long.

Lotus Notes 8 & Outlook 2000-2003

Since Lotus Notes 8 and Outlook are exclusively desktop clients, we can get away with forcing them to always show a fixed-width desktop-optimized view. We do that by targeting them with a conditional: Lotus Notes and older versions of Outlook use Internet Explorer as their rendering engine, so we check for IE, then insert a fixed-width constraining element around the entirety of the email’s content.

<!--[if IE]>
  <table width="540" align="center" cellpadding="0" cellspacing="0" border="0">
<!--[if IE]>

Note that the constraining element is a table. This is because, as we discovered, divs cannot be reliably centered in all email clients.

Outlook 2007+

Moving on, we can use the exact same strategy in Outlook 2007 and newer. The only difference is, Outlook 2007+ uses Microsoft Office as its rendering engine. Don’t ask why; just add gte mso 9 to the conditional. That bit of magic incantation there stands for “greater than or equal to Microsoft Office version 9,” which covers Office 2000 or newer:

<!--[if (gte mso 9)|(IE)]>
  <table width="540" align="center" cellpadding="0" cellspacing="0" border="0">
<!--[if (gte mso 9)|(IE)]>

Apple Mail

Unexpectedly, our testing revealed that Apple Mail also fails to respect max-width! Fortunately, it does understand media queries, so we can add a fallback. Put this in a linked stylesheet or between <style> tags in the head:

@media only screen and (min-device-width: 541px) {
  .content {
    width: 540px !important;

This just says, “If my device isn’t an itty-bitty phone, then make the whole thing fixed width.”

Done! Here is everything all together in one file.

Finally, remember to test test test. We test on  Litmus, our coworkers, and a small percentage of our real live users. Test until you can test no more, and then release it into the world!

Now we can go party!


* I should note that these two examples do use media queries for small refinements like tweaking the spacing and size of the graphics. However, if you view them on a mobile device that doesn’t understand media queries, the experience would still be pretty good. These layouts may use media queries, but they do not depend on them.

FogBugz On Demand Performance Upgrade

October 22nd, 2013 by Dane Bertram

Some months ago we launched a beta for an upcoming infrastructure upgrade of FogBugz On Demand. The response we received was great! Hundreds of you—our awesome customers—signed up to kick the tires on a shiny, new, kiwi-riding-an-ocelot fast FogBugz On Demand service.

When we first launched the beta there were a number of core features that hadn’t been finished just yet: custom fields, the working on menu, quick case add on the list page, etc. We can happily report that all of those features (and more!) have now been implemented! As a result, the beta is now closed and we’ve started rolling out the FogBugz On Demand Performance Upgrade (née “Project Ocelot”) to large swaths of our customers over the past few weeks. Thousands of you in fact.

How can I tell if I have the new hotness?

If you created your FogBugz On Demand account after August 5th, 2013 you’ve been using it the whole time! If not, have you seen this cheery little “Welcome to fast” dialog after logging into your FogBugz On Demand account?

"Welcome to Fast!" dialog from the FogBugz On Demand Performance Upgrade

If you haven’t, your account hasn’t been enrolled in the performance upgrade yet. We’re currently selectively enrolling customers that are an ideal fit given the features that have currently been completed in the new interface. Want to sneak a peek? Let us know your FogBugz On Demand account info and we can enable “the switcher” for you—a toggle for enabling and disabling the Performance Upgrade that lives on your user options page within FogBugz.

If the beta is closed, why haven’t you enrolled everyone?

Just because we’ve completed the beta, doesn’t mean the FogBugz team is off on a sandy beach somewhere sipping Kiwi Koolattas. We’re busy adding more features, porting top-used plugins, and fixing the bugs you’ve been reporting to us about the new, faster FogBugz On Demand and releasing juicy updates every week. Keep the feedback coming!

There are still plenty of things left for us to implement in the new interface. Currently only the list, case, and search pages have been super-charged and there are a number of existing features that haven’t been updated to work with the new interface just yet. In the coming months as we knock more items off our super-fast FogBugz-powered to-do list, we’ll be enrolling more and more accounts.

What makes the Performance Upgrade so fancy? Why’d it take so long? What’s the secret sauce?

Easy there, Captain Questions! We—the FogBugz team—are also planning to write up a bunch of juicy blog posts telling not only the history of “Project Ocelot,” but also describing various parts of the underlying tech that power it. Hybrid modes, generated code, escape hatches, client-side MVC, elasticsearch, JSON APIs, and more!

A huge “Thank you!” again, to all of our faithful beta testers and a warm “Welcome to Fast!” for those of you whose accounts have already been enrolled in the FogBugz On Demand Performance Upgrade. We hope you like it!

What’s new in Kiln? AppVeyor web hook!

October 17th, 2013 by Kevin Gessner

Do you use AppVeyor to build and deploy your .NET projects?  If so, you’re in luck: Kiln’s web hooks now connect to AppVeyor!

AppVeyor web hook

AppVeyor can pull your code directly from Kiln.  Once you’ve created your project in AppVeyor, you can trigger a build manually, or set up the new web hook to trigger a build on every push.

AppVeyor builds

Building .NET projects has never been easier!

The AppVeyor web hook is available in Kiln 3.0.90 and higher.  Want to see Kiln integrate with another service?  Let us know!

Introducing name association in Kiln

October 2nd, 2013 by Hao Lian

My first Hotmail account was “hao2lian@hotmail.com” because at the time “hao” and “haolian” were taken and I didn’t know any better than to add an ungainly two in the middle of my name.

Then when I discovered IRC my mom, bless her heart, told me I could have any handle I wanted as long as it didn’t contain my real name. (I was 14 at the time, and Edward Snowden was 21.) I wanted to keep being hao2lian, but I also wanted my mom to be happy. So I conceded.

Now I had two identities. And I was hooked, hooked on coming up with better and better nicknames for myself until I had to start tracking all my usernames in a text file, which by the end of the aughts included multiple Yahoo and Gmail email accounts. Thank goodness for Dropbox when it finally came along.

So it’s not surprising that name and email association is one of the hotly requested features for Kiln. We took a while, but we finally got around to it. As part of his last summer’s Kiln internship, Josh Cooper spruced up the changeset page with this nifty link, which you’ll see if you’re an administrator on your Kiln account:

The name association popup

Were you to click on the link and choose a Kiln user

A successful name association

you will have taught Kiln that the username maps to that Kiln user! From that day forward, all commits with that username will become linked to that person in your Kiln account. Kiln beams at you. Armed with this new information, it can connect up your search results, revset filtering, review data, and more to the right Kiln account. All for you, and your ever-mercurial internet identity.

Hao Lian is a programmer on the Kiln team. Did you know about the ArtisanVideos subreddit?

Introducing the Kiln Command Line Tool for Git

September 25th, 2013 by Benjamin Pollack

We made Kiln into the best enterprise Git hosting tool in March, and we’ve been happily shipping Git and related utilities as part of the Kiln Client Tools since then. But there was always one component missing: an equivalent of the hg kiln command-line tool.

We know that many users prefer to use GUIs, but for those of us who are wedded to our command lines, having a quick and easy way to work with Kiln without leaving the keyboard is a godsend. From Mercurial, this is pretty easy: want to see a list of related repositories? That’s just hg kiln -t. Want to make a new repository to push your changes to? A quick hg kiln -n MyBranch and you’re good. Want to see a given file annotated with Kiln’s multilevel line annotations? hg kiln -a FooBar/Baz.cs and your browser pops up before you have a chance to spill your coffee. But since hg kiln was implemented as a Mercurial extension, Git users were left out in the cold, with no equivalent functionality.

No more. Now, Git users now have the git kiln command to access equivalent functionality. Want to see related repositories? That’s a simple git kiln related. You can view commits in Kiln via git kiln show (which takes everything Git understands, including even weird things like HEAD^^), where you can open reviews, link bugs, or view them in the Electric DAG. You can create branches in Kiln via the simple git kiln create-branch command. You can even do something your Mercurial colleagues can’t yet do: look up the Mercurial changeset that corresponds to the a Git commit, with the handy git kiln hg-sha command. There’s a lot more built-in, too; you can get the full details via git kiln help.

While we’re only distributing this first release of git kiln with our Windows Kiln Client tools, git kiln already runs on Mac and Linux. We’ll be working in the coming weeks to provide an easy way for Mac and Linux users to install it. In the meantime, git kiln is fully open-source and easy to build with Go, so feel free to take a look, play with it, and even submit a pull request if you’re so motivated.