Breeding Management System Dev Diary #1: Why I Chose TypeScript
The language selection process for developing a breeding system. A summary of how I reviewed and chose a language based on three criteria.
Introduction
When I started developing the breeding management system, the first decision I had to make was the choice of programming language.
Although I had been using TypeScript for four years at the time, I wanted to explore new languages. I reviewed Go, Kotlin, and the Erlang family, and ultimately returned to TypeScript.
Language Selection Criteria
A breeding center requires a large initial investment. In a situation where server costs had to be minimized, the criteria for language selection were as follows:
- Lightweight - Minimizing server costs
- Development Productivity - Solo development
- Domain Expressiveness - Clear expression of complex breeding logic
Review of Candidates
Go
From a Linguistic Perspective
Go is lightweight, fast, and simple to deploy. However, I felt that Go was more suitable for lightweight network I/O systems. Due to its limited support for functional expressions and object-oriented programming, I judged that my skills were insufficient to clearly express the complex real-world problems of parrot breeding into domain logic.
type Gender string
const (
Male Gender = "MALE"
Female Gender = "FEMALE"
UnknownGender Gender = "UNKNOWN"
)
type ParrotStatus string
const (
Feeding ParrotStatus = "FEEDING"
Weaned ParrotStatus = "WEANED"
Breeder ParrotStatus = "BREEDER"
)
type Parrot struct {
Aggregate // To use something like an abstract class with audit columns, you need to embed it like this.
ID string
Name string
Gender Gender
Status ParrotStatus
ImportedAt time.Time
}
func (parrot *Parrot) wean() error {
if parrot.Status != Feeding {
return fmt.Errorf("parrot is not feeding")
}
parrot.Status = Weaned
return nil
}Parrot model simply implemented in Go
ORM
Most Go ORMs are centered around query builders or raw queries. While GORM is said to avoid some of these issues, I felt its features were somewhat lacking compared to the ORMs I had used before.
Since the breeding system involves complex relationships between real-world domains, I decided that Go's ORM was unsuitable for me in terms of productivity.
Kotlin
From a Linguistic Perspective
I think Kotlin is a sophisticated language. It has excellent expressiveness and modern syntax. Although I'm not very close with Java, I've done a project or two with Kotlin, so it was somewhat familiar, and I think it's a well-designed language. However, unlike TypeScript, it is more object-oriented, which I thought might slightly reduce productivity for rapid implementation.
enum class Gender {
MALE, FEMALE, UNKNOWN
}
enum class ParrotStatus {
FEEDING, WEANED, BREEDER
}
class Parrot(
val id: String,
val name: String,
val gender: Gender,
var status: ParrotStatus,
val importedAt: LocalDateTime
) : Aggregate() {
fun wean() {
if (status != ParrotStatus.FEEDING) {
throw IllegalStateException("parrot is not feeding")
}
status = ParrotStatus.WEANED
}
}Parrot model simply implemented in Kotlin
JVM
The JVM consumes a lot of memory. Build times are also slow, and it's burdensome to run on a small server alongside other tools. In a breeding center with high initial costs, I didn't want to face issues like having to scale up the server just because of the language choice. The Spring ecosystem was also a bit difficult for me. While it might be good to have things strictly defined like in Spring, I thought its abstractions were so well-done that it was hard for me to understand the underlying principles. Of course, if I were running a breeding center, I'd have plenty of time at night, so I could study then... :)
Gleam
From a Linguistic Perspective
The Erlang VM is known for building robust systems. When I actually tried a simple project with Gleam, I found its simple syntax and functional characteristics led to good development productivity. I especially liked the pattern matching and functional programming style. Coming from TypeScript, Gleam's pattern matching seemed revolutionary.
pub type Gender {
Male
Female
Unknown
}
pub type ParrotStatus {
Feeding
Weaned
Breeder
}
pub type Parrot {
Parrot(
id: String,
name: String,
gender: Gender,
status: ParrotStatus,
imported_at: DateTime,
)
}
pub fn wean(parrot: Parrot) -> Result(Parrot, String) {
case parrot.status {
Feeding -> Ok(Parrot(..parrot, status: Weaned))
_ -> Error("parrot is not feeding")
}
}Parrot model implemented in Gleam
Functional Language
While being a functional language helped with productivity, the breeding system involves complex logic like genetic tracking and coefficient of inbreeding calculations. Since I had mostly used object-oriented languages, I wasn't confident that I could clearly express such complex logic using functional programming. Additionally, I felt Gleam's ecosystem was still too immature. As a relatively new language, there weren't many references to look to, so I decided to set it aside for now.
TypeScript
Back to TypeScript
This is the language I've been using since I first started web development before my first job, and it has remained my primary language throughout my life as a full-stack developer. The advantage of TypeScript is that it's an all-rounder with no significant missing pieces. Its domain expressiveness was excellent, blending object-oriented and functional styles appropriately. While its type system is weaker than other strongly-typed languages, that very weakness boosted productivity, and I didn't find it "weak" enough to be a problem. The ecosystem is also one of the largest in the world, which significantly enhanced productivity.
export const gender = ["male", "female", "unknown"] as const;
export type Gender = (typeof gender)[number];
export const parrotStatus = ["feeding", "weaned", "breeder"] as const;
export type ParrotStatus = (typeof parrotStatus)[number];
export class Parrot extends Aggregate {
id: string;
name: string;
gender: Gender;
status: ParrotStatus;
importedAt: Date;
wean() {
if (this.status !== "feeding") {
throw new Error("parrot is not feeding");
}
this.status = "weaned";
}
}Parrot model implemented in TypeScript
Conclusion
Ultimately, I came back to TypeScript. It was a bit disappointing to miss out on the joy of learning a new language and its philosophy and principles, which I had enjoyed even before starting the business.
However, I am now going to run the breeding center as an entrepreneur, not just a developer. Rapid development and stable operation are higher priorities than technical exploration. Choosing a familiar tool was the rational decision. And there is still much to learn in TypeScript. Areas like advanced type system features and performance optimization remain vast.
New languages are something I'll try if there's a suitable project for them. Currently, I'm using a separate Go server for image processing. I'm not strictly sticking only to TypeScript; I'm using each language where it can leverage its strengths. I'll cover this in more detail later.
In the next post, I'll talk about choosing a runtime and framework.