A deep dive into a 2021 Golang Taiwan community discussion about modernizing PTT BBS, exploring the architectural decision to build database-style interfaces rather than immediately migrating from filesystem storage. The conversation reveals critical trade-offs between legacy constraints, community-driven development, and the pragmatic path to modernization.

In January 2021, the Taiwanese Golang community engaged in a substantive discussion about modernizing PTT BBS, Taiwan's largest bulletin board system. The conversation, sparked by a recruitment post seeking volunteer developers for a "friend-making project" (unpaid), exposed fundamental architectural challenges facing legacy systems and illuminated a pragmatic approach to modernization that prioritizes interface design over wholesale migration.
The Core Problem: A System Built for Another Era
PTT BBS, like many legacy systems, suffers from severe architectural debt. The recruitment post and subsequent TG discussion identified twelve critical issues ranging from code quality to fundamental design limitations:
Immediate Technical Debt:
- Interface, business logic, and database code are completely intertwined, making UX/UI adjustments nearly impossible
- Zero comments and abysmal readability
- No testing infrastructure whatsoever
- No benchmarking capabilities, making architectural changes purely guesswork
Fundamental Design Limitations:
- 32-bit time representation (looming 2038 problem)
- DES-based password hashing (cryptographically obsolete)
- Heavy shared memory design preventing server distribution
- Inflexible index file storage
- Dead mail forwarding mechanism
- No real-time mobile notifications for messages
- IPv6 incompatibility
- Big-5 character encoding without emoji or Taiwanese Romanization support
- No image/audio/video support
The Architectural Debate: Filesystem vs. Database
The discussion revealed a classic architectural tension: whether to build modern interfaces atop legacy storage or migrate to a completely new foundation.
The Traditionalist View
Rayer Tung initially argued that the first priority should be converting PTT's filesystem-based storage to a real database: "Using FS as DB made sense 25 years ago, but it is very incomprehensible now." The argument centers on modern database advantages:
- B+-tree indexes for efficient lookups vs. PTT's letter-based folder distribution (which creates hotspots for common initials like 'e' and 'i')
- Built-in redundancy and partitioning for reliability
- Unified operation interfaces vs. custom file manipulation
- Modern backup strategies beyond "Sunday morning rsync"
The Pragmatic Compromise
Pichu Chen, the recruitment post author, advocated for a more measured approach: build database-style interfaces first, then experiment with SQL migration in parallel branches. This strategy addresses several realities:
Why Not Immediate Migration?
- Risk: "The risk of directly converting to SQL is higher" (Julian Chu)
- Unknowns: No performance benchmarks exist for either approach
- Community Dynamics: "This is a sunsetting thing" (Kevin Yang) - PTT's declining user base and registration freeze make massive investment questionable
- Data Complexity: Not everything fits neatly into SQL. Pictures, logs, and the existing URL structure (M.1611180581.A.E43 format) already provide unique keys
The Interface-First Strategy
The plan involves:
- Establish read/write interfaces for the existing BBS
- Create clear documentation and test cases (avoiding the "look at C code comments" anti-pattern)
- Build a parallel SQL implementation for comparison
- Use real performance data to drive the migration decision
The Golang Angle: Why database/sql and go-sql-driver/mysql?
The recruitment post's requirement to read these packages sparked confusion. The discussion clarified this wasn't about forcing SQL usage, but about style and architecture:
Pichu Chen's Concern: "I'm very worried that someone will directly port C Code into Golang Code, and then all the comments will be written to look at which function in the C Code, then it's GG XD"
Julian Chu's Insight: "It looks more like borrowing the concept and architecture of database/sql to provide a common interface, and swapping the driver can access BBS file data in different formats."
The database/sql package provides a driver interface pattern that could enable:
- Multiple storage backends (filesystem, SQLite, PostgreSQL) behind a unified API
- Clean separation of concerns
- Idiomatic Go code that modern developers understand
Filesystem as Database: Design Patterns from the Maple Era
The discussion revealed that PTT's filesystem design wasn't naive—it evolved from 20+ years of BBS development. Key insights:
The Original Design Logic
PTT's structure wasn't "using a database," but rather "discussing how to design a database":
- Directory-based indexing:
/usr/p/pichufor users, similar to Git's hash-based distribution - Per-article files: Each article is a separate file, enabling simple rsync backups
- Push text system: Developed to combat filesystem inefficiency (4KB blocks wasting space on 12-byte lines)
Modern Alternatives
Pichu Chen proposed hybrid approaches:
- Git-style hashing:
sha1(userid)→ first 2 digits as folder index for better distribution - Archival strategy: Identify cold articles (unmodified for >1 year), archive into large files, index positions in SQLite
- Compression: Add modern compression for space savings
The Core Trade-off
The discussion highlighted a fundamental systems design question: When does filesystem storage make sense?
Pichu Chen's CoreDNS example: "If the online OP needs to directly delete a certain lease record, he needs to issue a DELETE command, which is not faster than opening vim and deleting it."
This reveals that operational simplicity sometimes beats architectural purity. For certain workloads, direct file manipulation remains pragmatic.
Community-Driven Development Model
The recruitment post explicitly stated this was a "friend-making project" with no salary. The discussion reflected concerns about sustainability:
Kevin Yang: "PTT may be gone soon... Not open for registration, popularity has been declining."
Marcus Liu: "PTT has been calling for help... But to be honest, I think it's very difficult."
The Tokyo COVID-19 website example was cited as inspiration: "I still hope that this project can be completed by the majority of the community, rather than, like most open-source projects in Taiwan, being completed by a fixed number of 'gods.'"
This reflects a broader pattern in legacy system maintenance: how do you attract community contributions to aging systems with declining relevance?
The Path Forward: A Systems Engineering Perspective
The discussion concluded with consensus on a pragmatic, incremental approach:
Phase 1: Interface Definition
- Document required functions in text files
- Open GitHub issues for implementation
- Write tests alongside code
- Establish Go idioms and style
Phase 2: Parallel Implementation
- Maintain existing filesystem backend
- Build SQL backend in separate branch
- Benchmark both approaches
- Use data to drive migration decisions
Phase 3: Gradual Migration
- If SQL proves superior, migrate incrementally
- If filesystem remains viable, optimize existing approach
- Maintain API compatibility throughout
Key Takeaways for System Designers
This discussion offers several timeless lessons:
1. Interface Abstraction Enables Experimentation
By defining clean interfaces first, you can swap implementations without breaking consumers. This is the essence of the database/sql driver pattern.
2. Performance Claims Require Evidence "After the interface is established, try another branch that is implemented with SQL. After there is performance data, this problem will be easier to reach a conclusion."
3. Not Everything Needs a Database The debate over filesystem vs. database storage depends on:
- Access patterns (random vs. sequential)
- Operational requirements (direct file access vs. query language)
- Team expertise and tooling
- Scale and concurrency needs
4. Legacy Systems Have Hidden Wisdom PTT's 20-year-old design decisions (like the push text system) solved real problems that modern architectures might reintroduce.
5. Community Sustainability Trumps Technical Perfection A "good enough" system that attracts contributors beats a "perfect" system that nobody maintains.
Relevant Resources
- PTT BBS Source Code
- PTT File Structure Documentation
- MapleBBS 3.0 Presentation
- Original Recruitment Post (Moptt)
- Original Recruitment Post (PTT)
- PTT BBS List
- PTT g0v Hackpad
Conclusion
The PTT modernization discussion represents a microcosm of systems engineering challenges: balancing legacy constraints with modern practices, community dynamics with technical requirements, and theoretical improvements with practical realities. The chosen path—interface-first, evidence-driven, community-powered—offers a template for modernizing other aging systems without betting everything on unproven architectural shifts.
For distributed systems engineers, this case study demonstrates that sometimes the most important decision is what not to change immediately, and that building the right abstractions can be more valuable than rushing to the "right" technology.
This article is based on a January 2021 discussion in the Golang Taiwan Telegram group, organized and published with permission from participants. The conversation captures the technical and community challenges of maintaining legacy systems in the modern era.

Comments
Please log in or register to join the discussion