
- Role
- Product · Design · Engineering
- Timeline
- Jul 2024 · refreshed Feb 2026
- Built
- Archived Rails app
- Stack
- Rails · PostgreSQL · OpenAI
I built Fathership to answer everyday questions with a brief acknowledgment and one practical next step, then carried that approach through the chat, account history, and fallback states.
- Make small questions easier to askThe concept grew from conversations about advice people hesitate to seek.See the idea
- Give each reply a next stepAcknowledge the question, suggest one action, then invite a follow-up.Explore the exchange
- Carry the conversation forwardAccounts, history, and fallbacks keep the chat usable through interruptions.See what I built
- Design beyond the answerTiming, access, and recovery make practical guidance easier to return to.Read the reflection
01 · Context
Make a small question easier to act on.
My mother raised me on her own, and I did not feel that I had missed out on a father figure. In conversations with others from similar homes, though, I heard that a place to ask questions they might not normally raise could be useful. While learning full-stack development, I built Fathership to explore that idea through a calm, practical exchange.
That exchange needed an application around it. Someone returning to a conversation should find their history, a message in progress should look like one, and a missing model connection should not leave the chat hanging. Those ordinary states shaped the accounts, stored conversations, and fallback responses as much as the prompt itself.

02 · Feature set
Give each reply a manageable shape.
I wrote the response prompt around three moves: reflect what was heard, offer one practical action within the next day, and ask a useful follow-up. The time horizon and concrete examples kept the answer focused on a next move. For specified crisis terms, a local branch points toward outside support instead of generating a standard reply.
The interface follows the same rhythm. Prompt starters help someone begin, a character count makes the input limit visible, and pending feedback explains the wait after sending. I bounded message length and conversation context so the model sees enough to continue the exchange without turning every reply into an open-ended transcript review.
Engineering noteKeep the chat usable when the model is unavailable
The external model is optional, so a missing token or failed request cannot be allowed to end the exchange without a response. The chat model bounds input and context, retries transient failures, and can return a local supportive message instead.
- Messages are limited to 700 characters by default and model context is limited to the latest eight turns.
- The API path uses configurable timeouts, token limits, and bounded retries.
03 · What I built
Carry the exchange through accounts and interruptions.
I used Devise for registration, sign-in, and account recovery, then loaded each chat through the signed-in account. Returning to a conversation, editing it, or removing it therefore follows the same ownership boundary rather than relying on a global chat lookup.
Turbo Streams update messages and the chat list, while Stimulus keeps the composer, pending state, and scroll position in sync. Failed requests have a recovery path, local responses keep the exchange moving when the optional model service is unavailable, and a resource library gives someone material beyond the chat.
Make the exchange legible

Engineering noteScope conversation lookup to its owner
Authorization is enforced by loading chats through the authenticated user instead of retrieving a global record and checking it afterward.
- All chat routes require an authenticated user.
- Create uses current_user.chats.new; show, update, and destroy use current_user.chats.find.
- Deleting a user cascades to that user’s stored chats.
04 · Reflection
The answer is only one part of the conversation.
When I refreshed the project in 2026, the most useful lesson came from the moments around the generated answer. Account-scoped history lets a person return to the same exchange, progress feedback makes the wait legible, and fallback behavior gives the interface a way through service failure. Each one makes the original promise of a manageable next step more coherent.
I kept Fathership as an archived learning project. The narrow response format made the concept tangible, while the surrounding application showed me how much of a conversation depends on access, timing, and recovery rather than wording alone.