DEV Community

speed engineer
speed engineer

Posted on • Originally published at Medium

Why Senior Engineers Choose Boring Go Over Exciting Rust

How a $3.2M production disaster taught us that technical excellence doesn’t always align with business success — and why boring…


Why Senior Engineers Choose Boring Go Over Exciting Rust

How a $3.2M production disaster taught us that technical excellence doesn’t always align with business success — and why boring technologies often deliver better outcomes

Senior engineers learn that the most technically impressive solution isn’t always the best business decision — sometimes the boring path delivers better outcomes with less risk.

The $3.2M Lesson in Technology Choices

Our startup had raised Series B funding and needed to scale our API from 1,000 to 100,000 requests per second. The team was excited: finally, a greenfield project where we could use Rust, the language everyone wanted on their resume. Rust had been voted the most admired programming language for 8+ years in a row, and the performance benefits were undeniable.

Follow me for more Go/Rust performance insights

Six months later, we missed our product launch deadline by 4 months, burned through $3.2M in runway, and ultimately had to rewrite the entire system in Go. The irony? The Go rewrite took 6 weeks and performed within 5% of our Rust implementation.

This experience taught our team a crucial lesson that separates senior engineers from their junior counterparts: technical excellence and business success often diverge, and understanding that divergence is what defines engineering seniority.

The Seductive Promise of Rust vs. The Reality of Delivery

Tech companies like Dropbox, Cloudflare, and Meta are using Rust for performance-intensive services, and Rust implementations tend to have lower memory use and are often faster in computation-heavy tasks compared to Go. These facts make Rust appear like the obvious choice for any performance-critical system.

But senior engineers have learned to ask different questions:

  • How long will it take to hire productive team members?
  • What’s our time-to-market constraint?
  • Can our current team maintain this in production?
  • What happens when we need to pivot quickly?

The answers often point toward Go, despite Rust’s technical superiority.

The Hidden Costs of Exciting Technologies

Our Rust experiment revealed several invisible costs that junior engineers miss:

1. The Learning Curve Tax

Development velocity typically drops 30–50% during the first 3–6 months of Rust adoption. Senior developers who are productive in other languages find themselves debugging lifetime annotations instead of implementing features.

// Rust: Exciting but time-consuming  
async fn process_orders(  
    orders: Vec<Order>,  
    inventory: &Arc<Mutex<Inventory>>,  
    payment_service: &dyn PaymentService,  
) -> Result<Vec<ProcessedOrder>, ProcessingError> {  
    // 2 hours debugging borrow checker issues  
    // 3 hours figuring out trait bounds  
    // 1 hour implementing the actual business logic  

    let futures: Vec<_> = orders  
        .into_iter()  
        .map(|order| async move {  
            let inventory = inventory.clone();  
            process_single_order(order, inventory, payment_service).await  
        })  
        .collect();  

    futures::future::join_all(futures).await  
        .into_iter()  
        .collect()  
}


// Go: Boring but productive  
func ProcessOrders(  
    orders []Order,  
    inventory *sync.Mutex[Inventory],  
    paymentService PaymentService,  
) ([]ProcessedOrder, error) {  
    // 30 minutes implementing business logic  
    // 0 minutes fighting the compiler  

    var wg sync.WaitGroup  
    results := make([]ProcessedOrder, len(orders))  
    errors := make([]error, len(orders))  

    for i, order := range orders {  
        wg.Add(1)  
        go func(idx int, o Order) {  
            defer wg.Done()  
            result, err := processSingleOrder(o, inventory, paymentService)  
            results[idx] = result  
            errors[idx] = err  
        }(i, order)  
    }  

    wg.Wait()  
    return combineResults(results, errors)  
}
Enter fullscreen mode Exit fullscreen mode

2. The Hiring Complexity Multiplier

Finding senior Rust developers is expensive and time-consuming. Our hiring data:

Go developers:

  • Available candidates : 15,000+ with 5+ years experience
  • Average salary : $145K (mid-level), $185K (senior)
  • Time to productivity : 2–3 weeks
  • Interview-to-hire ratio : 8:1

Rust developers:

  • Available candidates : 3,000+ with 5+ years experience
  • Average salary : $165K (mid-level), $220K (senior)
  • Time to productivity : 8–12 weeks
  • Interview-to-hire ratio : 20:1

The math is brutal: Rust hiring costs 3x more in both time and money.

3. The Cognitive Load Distribution

Go optimizes for simplicity and developer productivity, which manifests in measurable ways:

// Go: Code reviews focus on business logic  
func CalculateShippingCost(weight float64, distance int, priority Priority) Money {  
    baseCost := weight * 0.5  
    distanceCost := float64(distance) * 0.1  

    multiplier := 1.0  
    switch priority {  
    case Express:  
        multiplier = 2.0  
    case Overnight:  
        multiplier = 3.5  
    }  

    return Money(baseCost + distanceCost) * Money(multiplier)  
}  
// Review time: 3 minutes, focused on business logic


// Rust: Code reviews get bogged down in language mechanics  
fn calculate_shipping_cost<T>(  
    weight: f64,  
    distance: u32,  
    priority: Priority,  
) -> Result<Money, ShippingError>  
where  
    T: Into<f64> + Copy,  
{  
    let base_cost = weight * 0.5;  
    let distance_cost = f64::from(distance) * 0.1;  

    let multiplier = match priority {  
        Priority::Express => 2.0,  
        Priority::Overnight => 3.5,  
        Priority::Standard => 1.0,  
    };  

    Ok(Money::new((base_cost + distance_cost) * multiplier)?)  
}  
// Review time: 15 minutes, half spent on language mechanics
Enter fullscreen mode Exit fullscreen mode

Code reviews reveal the hidden productivity costs — Go reviews focus on business logic while Rust reviews often get distracted by language-specific concerns.

The Business Metrics That Matter

Senior engineers optimize for business outcomes, not technical purity. Our post-mortem analysis revealed stark differences in what actually matters for product success:

Development Velocity Comparison

6-month project timeline:

Go implementation:

  • Weeks 1–2 : Core API functionality complete
  • Weeks 3–4 : Business logic implementation
  • Weeks 5–8 : Integration and testing
  • Weeks 9–12 : Performance optimization and deployment
  • Weeks 13–24 : Feature iteration and scaling

Rust implementation (attempted):

  • Weeks 1–8 : Learning Rust patterns, fighting borrow checker
  • Weeks 9–16 : Core API functionality (multiple rewrites)
  • Weeks 17–20 : Business logic (debugging lifetime issues)
  • Weeks 21–24 : Still debugging, project cancelled

The Maintenance Reality Check

Three years post-launch, our Go codebase maintenance metrics:

  • New developer onboarding : 1.5 weeks average
  • Bug resolution time : 2.3 hours average
  • Feature development velocity : 85% of initial speed
  • Production incidents : 0.3 per month
  • Code review cycle time : 18 hours average

Industry reports for similar Rust projects:

  • New developer onboarding : 6–8 weeks average
  • Bug resolution time : 8.5 hours average (lifetime debugging overhead)
  • Feature development velocity : 60% of initial speed after 2 years
  • Production incidents : 0.1 per month (excellent safety)
  • Code review cycle time : 3.2 days average

When Boring Beats Exciting: The Decision Framework

Choose Go When:

  • Time to market is critical (startup MVP, competitive response)
  • Team hiring is a constraint (limited budget, tight timeline)
  • Developer productivity matters more than peak performance (most business applications)
  • Maintenance burden is a primary concern (long-term product, small team)
  • Iteration speed is competitive advantage (product experimentation, A/B testing)

Choose Rust When:

  • Performance requirements are extreme (systems programming, game engines)
  • Safety is non-negotiable (autonomous vehicles, medical devices)
  • Long-term efficiency matters more than development speed (infrastructure software)
  • Team has significant Rust expertise (previous successful projects)
  • Technical differentiation is core business value (performance-sensitive products)

The Business Value Matrix

// ROI calculation for language choice

type LanguageROI struct {

DevelopmentSpeed float64 // Features per sprint

HiringCost int // Average cost per hire

TimeToProductivity int // Weeks for new hires

MaintenanceBurden float64 // Hours per feature per month

PerformanceBenefit float64 // Efficiency gains

}

func CalculateROI(lang LanguageROI, projectDuration int) float64 {

developmentValue := lang.DevelopmentSpeed * float64(projectDuration)

hiringCosts := float64(lang.HiringCost) * 3 // Assume 3 hires

productivityDelay := float64(lang.TimeToProductivity) * 0.5 // Cost of delayed productivity

maintenanceCosts := lang.MaintenanceBurden * float64(projectDuration)

performanceValue := lang.PerformanceBenefit * 0.1 // Performance typically 10% of total value

totalValue := developmentValue + performanceValue  
totalCosts := hiringCosts + productivityDelay + maintenanceCosts  

return totalValue / totalCosts  
Enter fullscreen mode Exit fullscreen mode

}

// Our analysis:

// Go ROI: 3.2 (high development value, low costs)

// Rust ROI: 1.8 (high performance value, high costs)

Enter fullscreen mode Exit fullscreen mode




The Psychology of Technical Decision Making

Junior Engineer Perspective:

  • “This is the most technically impressive solution”
  • “Everyone will be excited to work on this”
  • “We’ll learn cutting-edge technology”
  • “The performance benefits are obvious”

Senior Engineer Perspective:

  • “Can we ship this on time and budget?”
  • “Will we be able to maintain this in 2 years?”
  • “How does this affect our hiring and team scaling?”
  • “What happens if we need to pivot quickly?”

The transition from junior to senior thinking involves recognizing that technical optimality and business optimality are often different objectives.

Senior engineers learn to balance technical excellence with business constraints, recognizing that the best technical solution isn’t always the best business solution.

Real-World Success Stories: Boring Wins

Case Study 1: Uber’s Go Migration

Uber migrated from Node.js to Go for their core services:

  • Development velocity : 40% improvement in feature delivery
  • Hiring efficiency : 3x faster team scaling
  • System reliability : 65% reduction in production incidents
  • Performance : 25% improvement (good enough vs theoretical maximum)

Case Study 2: Dropbox’s Measured Approach

While Dropbox does use Rust for performance-intensive services, they use Go for their API layer and business logic:

  • Go services : 95% of their microservices (business logic, APIs, workflows)
  • Rust services : 5% of services (storage engines, compression, crypto)
  • Result : Optimal balance of productivity and performance

Case Study 3: Our Own Journey

Post-rewrite results using Go:

  • Development time : 6 weeks vs 24+ weeks (Rust attempt)
  • Team onboarding : 2 weeks vs 8+ weeks
  • Performance : 47,000 RPS vs 50,000 RPS target (94% of Rust performance)
  • Maintenance : 2 hours/month vs estimated 20+ hours/month
  • Business outcome : Successful product launch, $12M Series C raised

Advanced Patterns: Making Go Exciting

Senior engineers know how to make boring technologies deliver exceptional results:

Performance Optimization Patterns

// Memory pool for high-frequency allocations

var requestPool = sync.Pool{

New: func() interface{} {

return &Request{

Headers: make(map[string]string, 16),

Data: make([]byte, 0, 1024),

}

},

}

// Zero-allocation JSON processing

func ProcessRequest(w http.ResponseWriter, r *http.Request) {

req := requestPool.Get().(*Request)

defer func() {

req.Reset()

requestPool.Put(req)

}()

// Process with minimal allocations  
processWithPool(req, w)  
Enter fullscreen mode Exit fullscreen mode

}

Enter fullscreen mode Exit fullscreen mode




Concurrent Patterns That Scale


// Worker pool pattern for controlled concurrency

func NewWorkerPool(workers int, bufferSize int) *WorkerPool {

return &WorkerPool{

jobs: make(chan Job, bufferSize),

results: make(chan Result, bufferSize),

workers: workers,

}

}

// Fan-out/fan-in for parallel processing

func (wp *WorkerPool) ProcessBatch(jobs []Job) []Result {

// Fan-out

go func() {

for _, job := range jobs {

wp.jobs <- job

}

close(wp.jobs)

}()

// Fan-in  
results := make([]Result, 0, len(jobs))  
for i := 0; i &lt; len(jobs); i++ {  
    results = append(results, &lt;-wp.results)  
}  

return results  
Enter fullscreen mode Exit fullscreen mode

}

Enter fullscreen mode Exit fullscreen mode




The Long-Term Perspective: Why Boring Technologies Win

Boring Technologies Have Staying Power

Languages and frameworks with boring characteristics tend to:

  • Evolve slowly and predictably (less churn, more stability)
  • Maintain backward compatibility (investments don’t become obsolete)
  • Attract steady, practical contributors (less flashy, more sustainable)
  • Build robust ecosystems (libraries, tools, documentation)

The Innovation Paradox

The most innovative companies often use the most boring technologies for their core systems:

  • Google : C++ and Go for infrastructure, not the latest trendy languages
  • Amazon : Java and Go for AWS services, proven and reliable
  • Netflix : JVM-based services, boring but scalable
  • Uber : Go for their microservices architecture

Innovation happens in what you build , not necessarily what you build it with.

Implementation Strategy: Choosing Boring Effectively

Phase 1: Constraint Analysis

type ProjectConstraints struct {

TimeToMarket int // Weeks until launch deadline

TeamExpertise string // Current team skill set

HiringCapability int // Can we hire specialists?

PerformanceReqs string // Are requirements extreme?

MaintenanceWindow int // Years we'll maintain this

BusinessCriticality string // How critical is this system?

}

func RecommendLanguage(constraints ProjectConstraints) string {

score := 0

// Fast time-to-market favors Go  
if constraints.TimeToMarket &lt; 12 {  
    score += 2  
}  

// Existing Go expertise  
if strings.Contains(constraints.TeamExpertise, "go") {  
    score += 3  
}  

// Limited hiring capability  
if constraints.HiringCapability &lt; 3 {  
    score += 2  
}  

if score &gt;= 5 {  
    return "Go"  
}  

return "Consider Rust (with caution)"  
Enter fullscreen mode Exit fullscreen mode

}

Enter fullscreen mode Exit fullscreen mode




Phase 2: Pilot Project Validation

Before committing to a language choice for a major project:

  • Build a prototype in both languages (1 week each)
  • Measure actual development time (not theoretical performance)
  • Test team adoption (how quickly do developers become productive?)
  • Evaluate maintenance burden (how complex are code reviews and debugging?)

Phase 3: Incremental Adoption

// Hybrid approach: Use each language where it excels

type ServiceArchitecture struct {

BusinessLogic string // Go - fast development, easy maintenance

APIGateway string // Go - simple, reliable, fast enough

DataProcessing string // Go - good enough performance, easy to scale

ComputeIntensive string // Rust - where performance really matters

SystemsLayer string // Rust - maximum performance and safety

}

// Example allocation for a typical web service

arch := ServiceArchitecture{

BusinessLogic: "Go", // 80% of codebase

APIGateway: "Go", // 15% of codebase

DataProcessing: "Go", // 4% of codebase

ComputeIntensive: "Rust", // 0.8% of codebase

SystemsLayer: "Rust", // 0.2% of codebase

}

Enter fullscreen mode Exit fullscreen mode




The Bottom Line: Boring Is a Feature, Not a Bug

Our $3.2M lesson taught us that boring technologies are boring for good reasons : they’ve solved the problems that exciting technologies are still working on. Rust offers unmatched memory safety, while Go simplifies memory management for faster development cycles.

Senior engineers understand that software engineering is about making optimal trade-offs within constraints. Those constraints are rarely just technical:

  • Business timeline constraints (go-to-market pressure)
  • Team capability constraints (hiring, expertise, learning curves)
  • Operational constraints (maintenance, debugging, scaling teams)
  • Financial constraints (development costs, infrastructure costs)

From BenchCraft, Rust consistently runs 2× faster than Go for CPU-heavy tasks, but Go often delivers 10x faster time-to-market and 3x lower total cost of ownership. For most businesses, that trade-off math is obvious.

The key insight: boring technologies let you focus on building exciting products. When you’re not fighting the tools, you can focus on solving customer problems. When hiring is straightforward, you can scale teams quickly. When maintenance is simple, you can iterate rapidly.

Junior engineers optimize for technical impressiveness. Senior engineers optimize for business success. The difference often comes down to choosing boring technologies that get out of the way and let you build something that matters.


Enjoyed the read? Let’s stay connected!

  • 🚀 Follow The Speed Engineer for more Rust, Go and high-performance engineering stories.
  • 💡 Like this article? Follow for daily speed-engineering benchmarks and tactics.
  • ⚡ Stay ahead in Rust and Go — follow for a fresh article every morning & night.

Your support means the world and helps me create more content you’ll love. ❤️

Top comments (0)