Dec 19, 2024 Slack Development Lessons - Part 1 Navigating Slack's Unique Development Environment

Harrison Strowd

Slack Development Lessons - Part 1

For the past six weeks, we've been dedicating our “20% time” to prototyping a Slack application that brings the functionality of Marginal directly to teams in Slack. After sketching out our vision for how users might interact with Marginal within Slack, we dove into technical discovery and pieced together a prototype. The goal? To uncover and mitigate potential risks while we explored what's possible with Slack's development tools.

This hands-on process taught us several valuable lessons that will shape the future development of a full-fledged version of this application. In this post, we'll cover three key takeaways from our experience, but there's also more to come in a subsequent post. So keep an eye out for Part 2, where we'll share additional lessons from this exercise.

1. Breaking Habits Is Hard (But Sometimes Necessary)

After spending decades refining good engineering habits, abandoning them—even temporarily—can feel counterintuitive. Practices like:

  • Self-reviewing code before committing
  • Refactoring for clarity and simplicity
  • Writing meaningful documentation
  • Including quality test coverage

…are second nature to engineers aiming to deliver high-quality, maintainable work. These habits are invaluable when building production-ready applications impacting thousands or even millions of users. However, when prototyping, these practices can slow you down. The goal of a prototype isn't to ship polished, scalable code but to quickly experiment, learn, and reduce risks.

While I knew these habits were unnecessary for this prototyping exercise, I still ended up falling back into them for the first half of the six week cycle we alloted for this work. With 3 weeks burned, I had only established the Marginal data model in the new backend service. While well-documented, covered by tests, and ready for production (if it existed), progress was slow. At this point I finally cut ties with these habits and started hacking through the remainder of the technical discovery work. Over the course of the next week, I experimented with all the Slack integrations we identified as unknown or high risk, including interactive messages, ephemeral posts, modals, and message threads.

This experience highlights the significant productivity cost (5-10x) of developing features robustly versus quickly hacking them together. While this is certainly a high price to pay, in most cases it is a valuable investment in avoiding bugs, minimizing technical debt, and future engineers (i.e. your future self) from headaches while building on top of this work.

Lesson learned: Prototyping requires loosening the reins on perfectionism. And that's okay.

2. Limited Ruby Framework Support

Coming from the Rails community, we hoped to find a robust gem or Rails-like framework for building Slack apps in Ruby. Instead, we found a fairly sparse set of development tools available, with the Slack Ruby Bot Server standing out as the best option.

This server does a good job simplifying the Slack app setup operations (e.g., OAuth, event subscriptions) and handles core integrations like slash commands and interactive messages. There are also sample apps that provide helpful insights into using the framework for specific cases, such as consuming Slack events or integrating paid subscriptions with Stripe.

We greatly appreciated having the Slack Ruby Bot Server as a starting point to work from but for engineers coming from a Rails development environment, you may find some aspects of this framework more challenging than expected. In particular, we discovered:

  • The Ruby Slack Bot Server lacks prescriptive structure for organizing business logic. Without careful planning, complexity can spiral into chaos.
  • Key features, like modal submissions, are unsupported out of the box.
  • For teams with an existing Rails application, maintaining a standalone Slack backend service adds unnecessary overhead.

One potential workaround is mounting the Ruby Slack Bot Server within a Rails app, as outlined in this article. This approach might allow us to leverage Rails' strengths while integrating Slack's functionality. It's something we'll explore further.

Lesson learned: Ruby's Slack ecosystem is functional but far from plug-and-play. Be prepared for extra groundwork.

3. Slack Is Not The Web

After years of building web applications, we've internalized many assumptions that don't carry over to Slack. For example:

  • Audience scope: Web apps are built around the idea that each each page or request is served to an individual user. Slack apps, on the other hand, interact with an entire workspace, meaning actions and visibility aren't limited to a single person.
  • Error handling: On the web, a quick page refresh fixes many issues. In Slack, an error during an interactive event might mean you completely lose the opportunity to notify the user or update a message.
  • Workflow constraints: For instance, modals can only be triggered in response to specific events (e.g., button clicks or slash commands), and inviting a user or app into a channel isn't a supported event.

In many cases these quirks require some rethinking of workflows and user experiences.

For example, our initial plan was to pop a modal when the Marginal App is added to a channel, allowing the user to quickly and easily provide all the information necessary to configure a series of Marginal discussions for this team. However, since there are multiple events that can result in an app being added into a channel, Slack does not allow modals to be triggered from this event. Instead, we had to post a message into the channel that prompts the user to provide the necessary information there.

Initially, this channel setup message included two dropdown input fields and a submit button. However, Slack's interactive messages don't operate like a web form where all information is collected in the client's browser and then submitted to the server in a single event. Instead, each interaction with a single element is delivered individually. We dropped the submit button altogether and implemented server-side logic to detect if all required inputs were complete.

This makes some cases far more challenging to distinguish and handle. For example, when a user interacts with one of the two dropdowns we presented, is this an indication that they didn't see or don't want to make a selection for the other dropdown? Or are they simply working their way through the series of inputs and just haven't reached that one yet?

These are just a few examples of how our web development paradigms had to shift to meet Slack’s requirements.

Lesson learned: Slack's environment introduces unique challenges that demand creative solutions.

Parting Thoughts and Next Steps

Prototyping our Slack app taught us invaluable lessons about adapting workflows, navigating Ruby's limited Slack ecosystem, and working within Slack's unique constraints. These insights will guide us in building the full-featured app and hopefully save us and others from headaches along the way.

Stay tuned for Part 2, where we'll dive into more lessons and takeaways from our Slack app development journey. In the meantime, have you worked on developing a Slack app? Are there other pitfalls we haven't encountered... yet? If so, we'd love to hear from you! Let us know your thoughts in the comments below.

Leave a comment

Thoughts for our thoughts

We hope you find our writing useful and, perhaps, that it gives you something to think about. We read everything we receive and we'd love to hear from you.