This article explores my journey learning production-grade web development while creating Dancexam, a tool for dance communities to administer and grade progression tests. The application is live at dancexam.corradomazzarelli.com, with a demo at dancexam-demo.corradomazzarelli.com. The source code is available on GitHub.
- The Learning Challenge
- The Technology Stack Journey
- Key Technical Challenges
- Lessons Learned
- Conclusion
The Learning Challenge
After building the Dance DJ Assistant in Python/Flask, I wanted to push myself further by learning Rust and modern web development practices. Dancexam became my vehicle for this learning journey, presenting several challenges:
- Learning Rust’s unique concepts (ownership, borrowing, lifetimes)
- Understanding modern web architecture
- Implementing proper DevOps practices
- Managing a production application
The Technology Stack Journey
Why Rust?
Coming from Python, choosing Rust was intimidating but offered several benefits:
- Type Safety: Catch errors at compile time rather than runtime
- Performance: Near-C speed without manual memory management
- Modern Tooling: Cargo’s package management and build system
- Growing Web Ecosystem: Emerging frameworks like Axum
- Learning Opportunity: Deep understanding of systems programming
Learning Rust for Web Development
The Initial Struggle
The first weeks were challenging:
// Early attempts at handling web requests
async fn handle_submit(
State(state): State<Arc<AppState>>,
Form(form): Form<TestSubmission>,
) -> Result<impl IntoResponse, Error> {
// Fought with ownership, borrowing, and async concepts here
}
Key learning moments:
- Understanding ownership vs. borrowing
- Managing shared state with
Arc
- Handling async operations properly
- Implementing proper error handling
Choosing the Stack
After research and experimentation, I settled on:
- Axum: Lightweight, performant web framework
- HTMX: Modern frontend interactivity without JavaScript
- Askama: Type-safe templating
- PostgreSQL: Robust data persistence
- SQLx: Type-safe SQL queries
Learning each technology taught valuable lessons:
Axum
- Route handling and middleware
- State management
- Error handling patterns
- Request validation
HTMX
- Progressive enhancement
- Server-side rendering
- Dynamic UI updates
- Reducing JavaScript complexity
Askama
- Type-safe template rendering
- Rust macro system
- Build-time template validation
DevOps Learning
The project pushed me to learn modern DevOps practices:
Docker & Containerization
# Learned multi-stage builds for efficiency
FROM rust:1.70 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bullseye-slim
COPY --from=builder /app/target/release/dancexam /usr/local/bin/
CMD ["dancexam"]
CI/CD Pipeline
- GitHub Actions for automated testing
- Automatic deployment on merge
- Environment management
- Secret handling
Database Management
- Migration management
- Backup strategies
- Connection pooling
- Query optimization
Key Technical Challenges
Type-Safe Database Queries
Learning to use SQLx’s compile-time checked queries was a game-changer:
// Compile-time SQL verification
let test_result = sqlx::query!(
"SELECT * FROM test_results WHERE id = $1",
id
)
.fetch_one(&pool)
.await?;
Async Programming
Understanding Rust’s async/await required learning:
- Future traits
- Async runtime (Tokio)
- Proper error propagation
- Resource management
Configuration Management
Implementing flexible test definitions through YAML:
tests:
-
metadata:
test_name: "Standard Leader Test"
config_settings:
live_grading: true
show_point_values: true
minimum_percent: 0.60
max_score: 98
tables:
- sections:
- name: "Pattern Scoring"
scoring_categories:
- name: "Footwork"
values: ["Perfect", "Variation?", "Right Concept", "Nope"]
- name: "Timing"
values: ["On", "Off"]
competencies:
- name: "Starter Step"
scores:
- [3, 2, 1, 0]
- [1, 0]
failing_score_labels:
- scoring_category_name: "Footwork"
values: ["Nope"]
Lessons Learned
- Type Systems Matter: Rust’s type system caught countless errors before production
- Testing is Crucial: Automated testing saves time and reduces stress
- Documentation is Key: Clear documentation helps both users and future maintainers
- Start Simple: Building features incrementally keeps the project manageable
- Community Matters: The Rust community was invaluable for learning
Mistakes Made
- Over-engineering: Initially tried to make everything perfect
- Insufficient Planning: Should have spent more time on architecture
- Delayed Testing: Should have implemented testing earlier
- Complex Deployments: Initially made deployment too complicated
Conclusion
Building Dancexam was a transformative learning experience. It pushed me far beyond my comfort zone with Python/Flask development into the world of systems programming and production web applications. The journey from concept to production taught valuable lessons about software architecture, testing, deployment, and maintenance.
Most importantly, it showed that diving into challenging technologies like Rust, while initially daunting, can lead to more robust and maintainable applications. The project continues to serve the dance community while providing ongoing learning opportunities in web development and systems programming.
For developers considering a similar journey, I recommend:
- Choose technologies that push your boundaries
- Build something useful for a real community
- Focus on fundamentals before optimization
- Embrace testing and automation early
- Document your learning process
The application continues to evolve, serving as both a practical tool for dance communities and a platform for exploring new technologies and techniques.