Please… Enough with the Sloppy, One-Shot SaaS Replacements — Mahmoud Abdelwahab, Railway
Read the talk
Please… Enough with the Sloppy, One-Shot SaaS Replacements
Mahmoud Abdelwahab’s internal marketing app at Railway shows how much work begins after a feature runs: preserving edits, making actions reversible, sharing useful views, reusing good patterns and testing the experience against running infrastructure.
From a talk by Mahmoud Abdelwahab
At a glance
Ideas worth remembering
A structured codebase and explicit reference pages give agents existing patterns to reuse across features.
Specify product behavior alongside functionality: non-blocking autosave, unsaved-change protection and optimistic updates with rollback.
Delayed deletion creates an undo window; archiving supports reversible removal from everyday views.
Package recurring preferences as skills, then verify the feature end to end against running infrastructure.
A working replacement still has to earn daily use
Sending an email or publishing a post is only part of replacing a marketing tool. The replacement also has to feel good when someone opens it every day. Mahmoud Abdelwahab opens with that gap: agents can produce working software in one shot, while the software people want to use depends on care that a feature request may never specify.
At Railway, his replacement is an all-in-one internal marketing app. It manages website content, the changelog and blog; sends email; posts across social platforms; and manages website media. A small CRM tracks external creators. Task tracking, launch planning and a calendar coordinate what goes out and when. Comments and real-time editing support the people doing that work together. Agents built the app, and Railway runs it with automatic deployments.
Recorded from an outdoor bench in Edinburgh in October 2026—a “ten out of ten bench”—this is a set of opinions earned while building that tool. Abdelwahab explicitly leaves room for those opinions to change as agents improve. The useful question is immediate: what must you ask for to turn a functioning internal app into a consistent, polished place to work?
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Give the agent patterns worth repeating
“Tech stacks don’t matter” needs a qualification. An agent may be able to work in many languages and frameworks, but the structure of the codebase still affects what it produces. Solid abstractions give a new feature an existing way to fetch data, organize code and present behavior. Without those patterns, each feature gives the agent another opportunity to invent a different solution to the same problem.
Abdelwahab’s preferences are TanStack libraries for web development and Payload for content-related work. The reason for choosing a CMS is practical: being able to build one from scratch does not make that the best use of effort. Established abstractions provide a starting point the agent can reuse. His broader recommendation is a properly structured codebase, rather than a claim that these particular libraries are mandatory.
The same distinction appears in the product. A server-rendered page with a submitted form can send an email or publish content. It satisfies the basic operation. Recreating a favorite SaaS product exposes the smaller decisions that make repeated use pleasant, and Abdelwahab finds that agents often leave those decisions out unless they are requested explicitly.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Save progress without interrupting the writer
A document editor makes the first missing behavior obvious. Requiring a Save click puts responsibility for preserving progress on the writer. Autosave moves that work into the background, but it also needs an interaction requirement: saving must be non-blocking. The writer should keep typing while the app saves, rather than waiting for each operation to finish.
A second layer guards against closing the browser tab while changes remain unsaved. These mechanisms address different moments: background saving preserves progress during work, while the close guard intervenes when the user tries to leave before saving finishes. They reduce opportunities to lose edits; the talk does not establish a guarantee against every browser, network or storage failure.
Real-time collaboration extends the editor to concurrent edits. In Abdelwahab’s experience, the agent could add it after a discussion and review of how it would work. His joke about “seven hundred million tokens later” preserves some of the effort behind that success. The recording does not explain a conflict-resolution algorithm or synchronization protocol, so the transferable lesson here is to specify collaborative behavior explicitly, rather than to copy an implementation.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Make suitable actions feel immediate—and recover from failure
Optimistic updates change when the interface acknowledges an action. Instead of showing a loading spinner until the operation completes, the app immediately displays the expected success state and synchronizes the change in the background. If synchronization fails, it rolls the interface back and tells the user the operation did not work. The fast path and the recovery path belong to the same feature.
That makes an action feel faster without claiming that the underlying operation finishes faster. It also requires judgment. Some actions should retain a loading state until success is confirmed. Abdelwahab recommends asking the agent to plan where optimistic updates make sense, rather than applying them indiscriminately. Knowing to request the behavior is part of building the product.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Separate removing an item from destroying it
A request to delete records often produces a Delete button, perhaps a confirmation dialog, and then deletion. Undo changes the experience. Clicking Delete immediately removes the record from the interface, while a short window lets the user change their mind. If they choose Undo, the record returns. If they do nothing, the app performs the deletion after the window expires.
When does deletion actually happen? The branch below separates the immediate visual response from the delayed operation. Both outcomes begin with the item disappearing, but only the expired undo window leads to deletion. This is a specific timing decision: the implementation described delays the operation, so Undo can restore the item before that operation occurs.
Archiving handles a different intent: someone wants an item out of the interface or search results without losing the ability to bring it back later. Archive and Unarchive give that intent its own operations. A short undo window helps with an immediate change of mind; archiving provides a reversible way to keep a record out of everyday view.
Immediate removal, delayed deletion
Read the diagram as text
- Click Delete. The user asks to remove a record.
- Hide item; offer Undo. Remove the item from the UI and allow a short time to change course.
- Restore item. Undo brings the item back.
- Perform deletion. Run the delayed operation when the user leaves the action unchanged.
- Click Delete → Hide item; offer Undo: Immediate UI response.
- Hide item; offer Undo → Restore item: User chooses Undo.
- Hide item; offer Undo → Perform deletion: Window expires without Undo.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Reuse an existing page, then package recurring preferences
Once a page works well, it can become a concrete reference for the next feature. Ask the agent to follow its UI, data fetching and overall structure. Two areas that both list, create, update and delete records should offer a consistent experience. A reference page gives the agent a local example of what that means, extending the earlier idea of reusable code patterns into reusable product decisions.
References improve consistency in Abdelwahab’s experience, but they do not ensure compliance: sometimes the agent ignores the named page. Recurring opinions can also be packaged as agent skills, so the agent has guidance to apply across features. Reference pages supply examples; skills carry reusable instructions.
His internal tooling draws on three kinds of guidance:
- UI and written feedback. One skill covers interface quality, typography, layouts and better error messages.
- Motion. Another set of design engineering skills guides animations and related interaction details.
- Deployment. Railway agent skills help the agent work with and deploy the infrastructure running the tool: buckets, databases, the application and the real-time server.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
The final check is using the running feature
A passing build and passing tests can still accompany a broken experience. That is why verification is the final—and, for Abdelwahab, most important—step. The agent needs to try the feature, observe what fails and fix it. Computer use made that form of verification substantially more useful in his workflow because the agent could exercise the application rather than stop at an implementation report.
The instruction includes the environment: run the infrastructure and verify the feature end to end. This asks the agent to test the feature in operation, where its pieces must work together. It is the practical check on all the earlier requests: the desired interaction has to survive actual use, not merely exist somewhere in the code.
The destination is an internal tool that works consistently and feels good enough to reach for. Abdelwahab closes by inviting builders to become their company’s “SaaS layer.” In this account, that role includes choosing the small behaviors, carrying them across the app and checking the running result—the work that turns generated features into something colleagues want to use.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
Related talks
- Beating RL With Reflection: GEPA and Optimize Anything
Extends the discussion of reusable agent skills by showing how execution feedback can produce repository knowledge that helps later attempts.
Read the complete timestamped transcript
- 0:00
People have been saying that SaaS is dead for a while now. It's not even a hot take at this point. Why pay someone to build and maintain the tools you use when you could just do it yourself? Just ask your agent to do it. And for me, there's one missing piece that rarely gets mentioned, and it's the fact that there's a gap, a big gap between working software that got one-shotted and software I would actually use, software that sparks joy, that is polished, that feels good. And I actually went on a journey to replace a bunch of SaaS providers we were
- 0:30
using internally at Railway for marketing purposes, and I just built an all-in-one tool that we use. The tool allows us to manage content for the Railway website, changelog, blog. We can use it to send emails, post on all of our social platforms at the same time, manage the website's media. There's even a mini CRM so that when we're working with, like, external creators, we can keep track of that. There is task tracking, launch planning, because, you know, you want to coordinate multiple things and when they'll go out. There's a calendar. And at the same time, there's,
- 1:00
like, comments, real-time editing, and it's all built using agents. I would say I felt the breakthrough when GPT-6 came out, and all of this is running on Railway. Everything just auto deploys. And let me essentially now walk you through the principles I use to actually make sure that when I want to add a feature, it's, you know, actually polished and matches the rest of the app, and at the same time, the principles that you can pull
- 1:30
from and apply when building tools of your own. So instead of doing my talk from my boring office, I decided to switch things up and do it outside. It is absolutely beautiful. Right now, I'm in Edinburgh, Scotland, and it's just unreal. Like, I'm on a bench outside, and this is my view. Definitely a ten out of ten bench. Now, this type of talk for me feels a little bit different than what I'm used to, because I essentially will walk you through the journey of me
- 2:00
building our own internal tools and the opinions I've developed. But I also understand that the landscape changes very, very quickly. So, like, anything I say might change completely in, like, the span of months. Who knows? So I don't know. Consider this my disclaimer. Like, by the time of recording is fall 2026. It's October. And yeah. So anyway, w-with that out of the way, uh, let's talk about the first point,
- 2:30
which is tech stacks don't matter. Now, a lot of people say that it doesn't matter what programming language you use, what framework you use, you can just ask your agent to go do anything, and it will just be able to do it. And while I agree to some extent, what I found is when you're building a tool that you will actually use, w-when you give the agent a proper code base with solid abstractions, what ends up happening is whenever you're working, let's say, on a new feature, you end up with a much more consistent
- 2:59
result that is similar to what you've already built before. And the reason is, like, it's very simple. The agent doesn't need to reinvent the same, like, wheel every time it tries to add a new feature, or it doesn't need to introduce new patterns. It will just essentially follow existing patterns. And when it comes to, like, what you choose, I feel like it kind of doesn't really matter as long as you have a code base that is properly structured. But, like, my go-to choice today
- 3:30
is, one, TanStack libraries for anything when building on the web. And if you're building anything content related, I would say definitely go with Payload. Solid experience, great abstraction. I went down, like, a big rabbit hole of, like, comparing CMSs at some point because I don't believe that I should, uh, build it from scratch, even though I can. But again, who knows? Maybe the future will change. But yeah. And that's kind of, like, the main first item that I kind of ran into.
- 4:03
When you use, like, a lot of SaaS products out there that you actually love, that you use every day, when you're a user, there's, like, a lot of things you don't notice. But when you actually try to, like, replicate an experience, you quickly find out how much care and thought goes into different flows. So, like, something as simple as, oh, I want to be able to send an email or to publish something, you can easily do it by having, like, a good old, you know,
- 4:33
just like, server-side rendered app with, like, forms that you submit. But if you try to build something that is gonna be used every day, that you will reach for a lot, you're like, "Oh, it shouldn't feel that way." And there are kind of, like, a bunch of patterns that I found that agents don't typically do unless you explicitly ask them to, and this is my list. So we'll go through them one by one. And the first one is, like, autosave. I, I found that if, like, let's say I'm building some
- 5:03
form of document editor, autosave is kind of like a table stakes feature. So rather than manually clicking save to save things, you just ask it to automatically save progress in the background and to make sure that it's, like, a non-blocking action so that you can... Let's say if as you're typing, you're able to type, and then it just automatically saves. And one other kind of, like, step further that you could do is to
- 5:33
also prevent the browser tab from closing if there are unsaved changes. So it's kind of like these, these two layers that kind of guarantee that whatever it is that you're working on is just not gonna be lost. Now, you can also go a step further and to add, like, real-time collaboration so that you can have, like, concurrent edits. And again, my agent didn't struggle at all with adding the feature. It was like I don't know, asking about it,
- 6:03
reviewing how it would work, and then once that's done, just seven hundred million tokens later, then I had real time. So I would say in general, this is the first thing that I find agents just don't do well from a product experience perspective. Another pattern I found that agents completely forget to implement is optimistic updates. So rather than showing your users a bunch of loading spinner whenever they're taking any action, you essentially show them the success state, you know, optimistically, and in the background, you're going to
- 6:32
sync whatever change they made. Now, if the sync or the operation fails for whatever reason, you just roll it back and then tell them, like, "Oh, the operation didn't work." Now, of course, you shouldn't blindly have optimistic updates for everything. There are some scenarios where you want to show the user a loading spinner, and you're going to wait to check that everything worked properly. But in general, having optimistic updates makes the overall experience feel significantly faster, much snappier,
- 7:02
and it will just feel much more instant. So pretty sure you can just ask your agent like, "Hey, I would like to implement optimistic updates where it makes sense. Let's come up with a plan," and it will tell you all the areas where it makes sense to do. Again, it's like it's not difficult for the agent to build it. You just need to ask and know exactly what to ask for. Now, an extension of optimistic updates, in my opinion, is giving users the ability to undo certain actions. Now, for example, if you ask your agent to implement the ability to delete
- 7:32
records, what it will typically do is it will give you a delete button. You click it, might show a confirmation dialog, you hit Confirm, and then it will actually do the deletion. But a much nicer experience is actually allowing the user to undo that action. So they hit Delete, you immediately remove the item they wanna delete from the UI, but you tell them like, "Hey, you have like a couple of seconds where you could change your mind." And if they don't do anything, that means you actually want to delete the thing,
- 8:03
then you actually implement the operation. Otherwise, if they hit Undo, you just bring back the change. So it's kind of like you do the operation in the background as well. It's just kind of a delayed action, which kind of ties into the next point, which is archiving. You should tell your agent you want the ability to archive records and be able to unarchive them, because sometimes you want to hide something from the UI completely, or let's say from search results, and archiving will be the way to do it.
- 8:33
And you shouldn't force your user to be like, "Well, if you don't want to see this, you have to delete it." And a much better experience is just the ability to archive records. So I think it is a pattern that should definitely exist. Another big one for me is URL state. You should ask your agent to persist state in the URL whenever it makes sense, of course. And it's so useful because whenever you have, let's say, a list of records and you want the ability to filter or sort them and you just have a bunch of like conditions, maintaining these
- 9:03
things in the URL makes it trivial for you to give them to someone else. And then let's say another team member is just gonna be able to see exactly what you want them to see. Now, another small detail that agents don't implement out of the box is infinite scroll. I don't want to be clicking on Load More over and over and over, or like a Next Page button. As I scroll, I should just get the data. I shouldn't need to click Next Page. And then in the end, I'll just see that, oh, I've reached the end. It's a very minor thing, but also when you use something every day,
- 9:33
I think it makes a huge difference. Now, another pattern I found that made the output significantly better is telling the agent explicitly to use existing pages as references when it comes to the UI, the data fetching and how it works, and how the thing is overall structured. And that made it possible for me to essentially define what good looks like once and then just reuse it for more features. Now, to be completely honest, sometimes even when you tell the agent like,
- 10:03
"Hey, use page X as a reference," sometimes it just doesn't follow it. I don't know if models like get dumber sometimes, but sometimes it does happen. But overall, I would say that my success rate is higher whenever I tell the agent to like, well, use an existing page as a reference, because you also just end up with a more consistent experience. So like, as you're using your app, you're like, "Oh, this just matches the other part of the app," if they both share like the same concept. So like if two parts of the
- 10:33
app are essentially lists of records that you can create, update, and delete and like list, well, both of these parts should be pretty much identical. This list kind of brings me to my next point, which is whenever you have opinions about something, they should be packaged as agent skills. And then this way, your agent, whenever it's building something, it will actually follow those skills pretty closely. And as I was building all of our internal tooling, I found myself reaching for three agent skills. Uh, two of them are related to design
- 11:02
engineering. So one made it easier to have better UI, better typography, better layouts, as well as like better error messages in general. And the second one was a set of design engineering skills that made it easier to just have better animations and other things like that. And then last but not least is the Railway agent skills, of course. So like everything that I built for us internally at Railway runs on Railway, of course. Like everything. So like buckets, databases, the app itself, the real-time server,
- 11:33
everything. And for the agent to effectively work with Railway and be able to deploy everything, you just give it the Railway agent skills, and it was just able to do it. And yeah, so like once you have all of your agent skills and everything, in my opinion, the final step, and I think this the most important step, is verification. So the agents typically, whenever you ask it to build something, it's gonna be like, "Okay, I implemented the feature, build passed, test passed, all good," right? But what I found is
- 12:03
when you actually try out what happens, you sometimes end up with just like a very broken experience. So once you have like a verification loop in place, it's like a night and day difference because the agent, you can literally see it be like, "Okay, I built this thing. I tried it. It failed. I'm gonna fix it," and it just keeps going. And then in the end it's like, "Okay, I fully tested it." And I felt this breakthrough with computer use once it got really, really good. And now everything-- every time I want to add a
- 12:33
new feature, I literally just in the end say, "Well, you should verify that the feature works end to end and use it by actually running infrastructure and to test things end to end." And the agents are very capable of doing this. You just have to tell them. And once you have all of those pieces, I would say you will end up with solid internal tools that you can build quickly, that work properly, and work in a consistent way. And that's it. This was my list of principles that I use whenever I'm
- 13:03
building any internal tools. Hope you found them useful. But more importantly, that you're inspired to build your own internal tools that feel good, that are polished, and spark joy. And I hope that you too can become your company SaaS layer. And with that, thank you so much for watching, and I'll see you in the next one.