You cannot press pause on development. That is the first thing to accept. Tickets keep arriving, customers expect shipments, and your existing code does not stop running just because you decided to document it. No engineering manager will greenlight a month-long freeze so the team can write the specification that should have existed from day one. OpenSpec was built for reality, not for greenfield fantasies. It works best when you bolt it onto what you already have, customers and all.
The goal here is not a rewrite. It is honest archaeology. You dig up what is actually running in production, describe it accurately, and let that description evolve as your code does. When your specification matches your system, you make life easier for the engineers who join next quarter and for the AI tools that now sit in your IDE. Here is how to do it without missing a single release.
Start With What You Actually Do
Open your repository and you will see folders named controllers, models, services, and utils. Those are technical layers, and they lie to you. They do not describe what your system does for the business. A folder full of JavaScript files does not explain how an order becomes a shipment. To retrofit OpenSpec, you need to think in capabilities.
Look for the stable business operations that would survive even if you rewrote the entire stack in a different language. In most product companies, these show up again and again: Orders, Billing, Inventory, Customers, and Notifications. Name five to eight of these core capabilities.
For each one, force yourself to answer five specific questions. What real-world problem does this capability solve? Where does the code actually live—one service, three microservices, or a legacy module no one wants to touch? What triggers it: a user click, a scheduled cron job, an inbound webhook? What data goes in and what data comes out? And finally, which other systems depend on it, meaning what breaks if this piece stops working?
Be brutally honest. If your "Customers" capability is smeared across a Rails monolith, a Node API, and an external CRM, write that down exactly. Your map has to look like the territory, not an architect's dream.
Write the Truth, Not the Wishlist
The most dangerous sentence in any documentation effort is, "While we're writing this down, we might as well fix it." Stop. You are not redesigning the checkout flow. You are describing the checkout flow that is charging real credit cards right now.
If placing an order triggers an immediate payment capture and then fires an email through a background worker, document that exact sequence. Do not insert an event queue you plan to add next quarter. Do not pretend the validation happens at the API edge if it actually lives deep inside a service class. Accuracy matters far more than aspiration.
Incorrect documentation is worse than none. It trains new hires to expect behavior that does not exist. It sends AI coding assistants down imaginary paths based on wishful thinking. When your spec matches production, you create a reliable baseline. Debugging gets faster because you stop guessing about the "intended" flow. Refactoring gets safer because you know the starting point is real.
Extract Contracts From Your APIs
Your API endpoints already enforce rules. They just keep them implicit. Retrofitting OpenSpec means pulling those rules into the open.
Start with inputs and validation. What does the endpoint actually accept? Document the types, the required fields, the maximum lengths, and the cross-field dependencies. Then describe the business behavior. Does this call create a record, trigger a side effect, or simply validate state against another service? Be specific.
Finally, catalog the responses. What does success return? What are the exact error codes and under what conditions do they appear? Do not write "returns an error." Write "returns 422 when the billing address is missing and 409 when the inventory was already reserved by another process." That level of precision turns a vague route into a contract that frontend teams, QA engineers, and automated tooling can trust.
Hunt Down the Hidden Rules
Sisteminizdeki en maliyetli bilgilerin bir kısmı boşluklarda gizlidir. Servis sınıflarının içindeki koşullu bloklara gömülmüş, veritabanı tetikleyicilerine sıkıştırılmış veya iki yıldır kimsenin dokunmadığı saklı yordamların (stored procedures) içine yazılmış olabilirler. Bunlar sizin iş kurallarınızdır ve genellikle sistem kesintileri sırasında veya en başından beri orada olan tek mühendisi köşeye sıkıştırarak yeniden keşfedilirler.
Onları gün ışığına çıkarın. Zaten bildiklerinizle başlayın. Belirli bir değerin üzerindeki siparişlerin ilerlemeden önce yönetici onayı alması gerekir. Pasif kullanıcı hesapları yeni sipariş oluşturamaz. İadeler yalnızca ödeme süreci tamamlanmadan önce yapılabilir. Her bir kuralı, yönettiği işlevin yanına, bir ürün yöneticisinin tercümana ihtiyaç duymadan okuyabileceği kadar net bir dille yazın.
Bu kuralları merkezileştirdiğinizde, onları sadece belgelemekle kalmazsınız. Tekrarları açığa çıkarırsınız. Çatışmaları ortaya koyarsınız. Ve tüm ekibe, birisi varlığını unuttuğunuz bir kısıtlamayı yanlışlıkla ihlal eden tek satırlık bir değişiklik yapmadan önce politikaları tartışabilecekleri tek bir alan sağlarsınız.
Tesisatı Haritalandırın
Modern sistemler olaylar (events) üzerinden çalışır. Bir servisteki eylem, kullanıcıya görünür bir şey ulaşmadan önce yarım düzine başka serviste dalgalanmalar yaratır. Bu dalgalanmaları çizmeniz gerekir. Temel iş akışlarınız için bir olaydan diğerine olan akışı haritalandırın. Sipariş oluşturuldu -> stok ayrıldı -> ödeme onayı bekleniyor. Bazı bağlantılar kırılgan görünse veya farklı protokoller kullansa bile tüm zinciri çizin.
Dahili trafikle sınırlı kalmayın. Onları öyle görmeseniz bile harici servisler sisteminizin bir parçasıdır. Her entegrasyon için amacını, uygulamanızın nasıl kimlik doğrulaması yaptığını ve nasıl hata verdiğini kaydedin. Ödeme ağ geçidi otuz saniye sonra zaman aşımına uğrayıp genel bir 500 hatası mı döndürüyor? Kargo API'si hafta sonları hatalı JSON mu döndürüyor? Kimlik sağlayıcı, yenileme jetonlarını (refresh tokens) kendi dokümantasyonunda belirttiğinden daha erken mi iptal ediyor? Bu ayrıntılar önemsiz görünür
