The Technical History of Disgo’s Development
Table of Contents
What Problem Does Disgo Solve?
Discord is a chat application that lets people communicate with each other. A bot is a software application that performs automated actions. So a Discord Bot is used to automate actions on Discord.
A Discord Bot is developed using a programming language with Discord’s Application Programmer Interface (API) to interact with Discord Servers. Software that interacts with an API (computer server) is called an API Wrapper.
Disgo is a Discord API Wrapper for the Go programming language. In other words, Disgo can be used by other Go programmers to quickly implement the ability to interact with the Discord API in their (Go) Discord Bots. Read “Why Go?” to understand why a programmer would use Go to create a Discord Bot.
Confused about APIs? Read the first section of “What is a Request?”, then watch “What are APIs?”, then watch “What is a REST API?”
Why Was Disgo Created?
Both DiscordGo and Disgo solve the same problem. However, DiscordGo has existed longer than Disgo, which prompts the question, “Why Was Disgo Created?” Personal experiences aside, the quality of DiscordGo’s developer operations is not high enough to keep up with the constantly updating Discord API.
A lack of updates to DiscordGo has resulted in missing features and relatively poor performance. Disgo was created as a response to DiscordGo’s shortcomings which are unsolvable due to the module’s backward compatibility promises. A modern comparison of DiscordGo vs. Disgo shows how Disgo is better than DiscordGo.
The Technical History of Disgo
Understanding the technical history of Disgo’s creation will provide you with the information you need to contribute to Disgo.
What is Dasgo?
A solution to a programming problem typically follows a pattern. When it comes to web services and applications, that programming problem is centered around data manipulation. For example, sending a Discord Message involves collecting data (i.e message content) and sending that data elsewhere.
The solution to a programming problem centered around data manipulation follows this pattern:
- Define the Data Model of the problem.
- Add Code Logic that manipulates the data (on a conditional basis).
For a concrete example of this pattern being implemented, read the steps required to add a Disgo Request. Steps 1 and 2 define the data models of a request. Step 3 sets up the generator (used to generate code logic). Steps 4 and 5 add the code logic that performs actions based on the provided data during runtime. Step 6 creates a test to validate the code logic.
Disgo must perform both steps for every feature of the Discord API. However, this becomes problematic when Discord has 180 requests and 70 events. In other words, an API Wrapper that implements every feature of the Discord API must define over 250 data models and the subsequent code logic for those data models.
So how does a Discord API Wrapper solve this problem?
DiscordGo implements features from the Discord API by defining the Go data models within the wrapper by hand. However, this requires the maintainer to define every data model correctly and also implement the code logic for it. The reality is that humans are prone to make mistakes. So, it’s no surprise that many of the (60+) issues on DiscordGo’s issue tracker result from incorrect definitions of a Discord API Data Model.
Define a Data Model
A Go API Wrapper must be able to marshal (convert) a Go Programming Language object to a JSON object.
In other words, data must be converted from this Go object:
type Emoji struct {
ID *Snowflake `json:"id"`
Name *string `json:"name,omitempty"`
Roles []Snowflake `json:"roles,omitempty"`
User *User `json:"user,omitempty"`
RequireColons *bool `json:"require_colons,omitempty"`
Managed *bool `json:"managed,omitempty"`
Animated *bool `json:"animated,omitempty"`
Available *bool `json:"available,omitempty"`
}
To this JSON object:
{
"id": "41771983429993937",
"name": "LUL",
"roles": ["41771983429993000", "41771983429993111"],
"user": {
"username": "Luigi",
"discriminator": "0002",
"id": "96008815106887111",
"avatar": "5500909a3274e1812beb4e8de6631111",
"public_flags": 131328
},
"require_colons": true,
"managed": false,
"animated": false
}
So you must adhere to certain rules while defining the Go object. When these rules are NOT met, certain configurations of the JSON object will be impossible to create: This is significant because the JSON object that a Discord Bot sends to Discord determines the actions of the respective request.
So to connect the dots…
Incorrectly creating a Go object results in missing features in a Discord API Wrapper. Instead of solving this problem by relying on a maintainer’s human accuracy, the Disgo founders created a specification for Discord Go objects. In modern times, it’s typical for the API Provider (Discord) to provide a machine-readable specification. However, Discord refuses.
Update (3/1/23): Discord has announced plans for an OpenAPI machine-readable specification.
The Discord API Specification
The Discord API Specification (DAS) was created to generate data models from the Discord API Documentation automatically. Unfortunately, we ran into many issues: #4. As a result, Dasgo was developed alongside its specification to provide Go objects that represent Discord Data Models.
A major benefit of this approach is that the Go objects maintain a direct representation of the Discord API Objects. This direct representation makes the Disgo API Wrapper a one-to-one implementation of the Discord API. In addition, Disgo is the only struct-based Discord API Wrapper (as opposed to a function-based Discord API Wrapper).
To understand the benefits of a struct-based API, read “I released vaporware” or the “Arikawa vs. Disgo Comparison”.
How Was Disgo Created?
Dasgo provides the data models used within the API Wrapper. Then, code logic is added to provide functionality for various features. In order to do this, we must pull the Dasgo objects into the Disgo codebase after each Discord API Update. So, I created generators to perform these tasks efficiently.
“What is a Request?” describes how Disgo requests work. Disgo requests are implemented with optimal rate limits, but this took time as the Discord Documentation for Rate Limits remains ambiguous. The Disgo implementation for rate limits is described in #14 and #22.
When you check the implementation of a few requests, you will realize that each request’s code logic is similar to other requests (with slight variations). Rather than code each request by hand, Copygen — a type-based code generator — is used to generate code based on the defined Dasgo types. As a result, the request code is automatically added following a Dasgo update via generators.
“What is an Event?” describes how WebSocket Session connections and events work for event handling. DiscordGo implements these features using reflection and type assertion, which results in suboptimal performance. Disgo avoids reflection and type assertion by having the user directly define the handler and by directly parsing the Event Payload (Binary or JSON) to its Go object.
This implementation also allows for Selective Event Handling.
In order to implement event handling code, Copygen is used to generate code based on the defined Dasgo types. As a result, the event handling code is automatically added following a Dasgo update via generators (in a similar manner to requests). Combining each generator operation into a single command gen allows the maintainer of Disgo to stay up-to-date with the Discord API in a low-effort manner.
Understanding Voice Connections
Voice Connections function in a similar manner to WebSocket Session Gateway connections. As a result, the code is similar to the WebSocket Session code.
This feature is still being implemented.
Understanding Sharding
Read “What is Discord Sharding?” for a simple yet full understanding of sharding on Discord.
This feature is still being implemented in #26.
Understanding Caching
Read “What is a Cache?” for a simple yet full understanding of the Disgo Cache.
This feature is still being implemented in #39.
Understanding Developer Operations
Every time a change is made to the codebase, we must verify that it doesn’t break functionality for end users.
- Static Code Analysis is used to ensure that code remains high quality.
- Unit testing ensures that code logic within the application remains error-free.
- Integration testing ensures the bot interacts with the Discord API in a production environment.
These changes run in a GitHub Actions pipeline that executes specified workflows on a virtual computer server every commit.
What is a Bundle?
A fieldalignment bundle is used to optimize the memory of the Dasgo structs. This optimization provides yet another performance boost compared to Disgo’s predecessors. For more information, read Implementing The Fieldalignment Bundle.
Why Is Disgo Significant?
Disgo’s performance improvements in the Go Discord API Wrapper ecosystem are fantastic. More significant is the time it took to develop the module: Version one took nine months, with three contributors total. Contrast that with DiscordGo, which has 150+ contributors and six years of development but still maintains many technical issues.
You can debate whether comparing new and old libraries (in the same niche) is fair. However, when that new library covers more features, is more performant, and is more tested, it’s hard to argue that Disgo is NOT a successor to DiscordGo.
What Challenges Does Disgo Face Today?
Officialization
Disgo aims to be mentioned in the Discord API Documentation. However, this requires the usage of Disgo in 1,000 users’ Discord Bots. If you are interested in assisting with this goal while gaining project experience in pull requests from other libraries, consider performing Roadmap Refactor tasks.
Brigading
Another challenge that Disgo faces is brigading. Certain DiscordGo members (or more so fans) have attempted to get the owner’s GitHub account banned. This group does NOT include the hard-working maintainer of that library, FedorLap.
This article will not focus on that situation, but it should be noted that various forms of misinformation have been spread about the Disgo maintainers to prevent its growth. Please do your best to prevent this misinformation so this software continues to grow.
How Do I Start Contributing?
Read the Disgo Contribution Guide for a complete guide on contributing to Disgo (without prior programming knowledge). Don’t hesitate to contact a maintainer if you need help at any point in this process.