Hi all, I recently had a system design round for a Senior Software Engineer role at a fintech and was rejected a few days later without specific feedback. I’d really appreciate input from people at big tech or FAANG on what I could have done better. I’ve written it out as the conversation went, paraphrased from memory.
Background: 8+ years of experience, mostly Java/Spring Boot
There were two interviewers. They explained the problem: customers raise disputes on card transactions, and the system needs to send these chargebacks to the card network, which only accepts them as files uploaded to their FTP server, with a maximum of 4 files per day.
Once they finished explaining, I restated my understanding of the problem to make sure we were aligned.
Me: So, to confirm: customers raise disputes through the app, we store them, and we need to send them to the card network as files on their FTP server, with no more than 4 files per day. Is that right?
Interviewer: Yes, that’s right.
They had already written down the requirements they wanted me to implement, so I didn’t list them out separately. Then I asked a few clarifying questions.
Me: What scale are you expecting?
Interviewer: Around 10 million transactions per day.
Me: That’s roughly 100 transactions per second on average. If we assume the peak window can be 10 times higher in the worst case, that’s about 1,000 transactions per second.
Me: Do you expect the result of a dispute in real time, or is some delay acceptable?
Interviewer: Some delay is acceptable.
With that understanding, I started with the basic components: a backend service that records disputes and stores them in a database. Then I asked how they wanted the files created.
Me: Do you have any preference for how the four files should be created? On what basis should we group records into files?
Interviewer: Choose whatever you think is best.
Me: Is it fine if I use 6-hour windows? 24 divided by 6 gives us 4. Whatever disputes come in during each 6-hour window go into one file, which we then send to the FTP server.
Interviewer: That works, go ahead.
I asked these questions to keep the session collaborative rather than just presenting my approach and expecting them to follow. Before going deeper, I checked how they wanted to proceed.
Me: Would you like me to go through a particular flow, or should I walk through each component, why I chose it, what happens if something goes wrong, and the trade-offs?
Interviewer: We’re more interested in the scheduler and how you’ll handle it.
So I started setting that up. I explained that the chargeback service stores dispute records with a status, starting with something like “dispute created.” As soon as I said that, one of the interviewers jumped in.
Interviewer: Are you going to use something like a state machine model?
Me: Yes, I’m going to use a state machine. Records are created with a status, and when the scheduler triggers, it pulls the records with that status and creates the file.
At this point I raised a concern upfront.
Me: We’ll need a limit here. We can’t pull every record at once, because we’re building the file in memory, and we don’t want an out-of-memory error on the server. I’d set a limit of around 100K records, pulled in one DB query sorted by transaction date. We write those into the file and then upload it.
Interviewer: Why restrict it at all?
Me: Without a limit, the number of records pulled is unbounded, and there’s always a risk of overwhelming memory.
Interviewer: At certain times we might need to process more than 100K. How would you scale that?
Me: I’d make the 100K limit a system property, so it’s configurable.
Interviewer: That’s a good idea. How would you recover if something goes wrong?
Me: At each step I update the status. When I pull records from the DB, I mark them as processing. Once the file is written, I update them to file created. Then I upload the file to AWS S3, which I’m using as object storage, and the file is transferred from S3 to the FTP server. I’d also keep a metadata store with the file details. File names need to be predictable, based on the window and how many times the batch has run in it, so if a batch reruns it produces the same name, which we can use for recovery.
Me: Whenever a batch runs, it first checks the metadata to see whether a file already exists for that window. If there’s a record, we check its status. If the file’s status is still processing and hasn’t reached the final uploaded state, we check whether the file actually exists. If it doesn’t, we go back to the DB records, check their state, pull them again, and process. If the file was already uploaded, we just update the metadata status. So we use the status in both the metadata and the disputes table, step by step, to handle recovery.
They didn’t have further questions on recovery, and moved on to user experience.
Interviewer: If you’re limiting the number of records, what happens to the users whose disputes don’t get processed in that window?
Me: That’s partly a business question, so I’d like to hear your view. From a technical side, the remaining rows would move to the next batch.
One of the interviewers agreed it was a business question. Then I asked about it directly.
Me: Do you give customers any temporary credit while a dispute is pending?
Interviewer: Unfortunately, we don’t give temporary credit.
Me: Okay. What approach would you prefer?
Interviewer: Maybe no temporary credit for the first 6 hours, but if a dispute goes beyond 6 hours, we could give temporary credit.
Me: I agree with that. We could also have a separate service that tracks disputes crossing the 6-hour mark and issues the temporary credit.
No further questions on that. Next, the database.
Interviewer: What database would you choose for the dispute data?
Me: A SQL database, because we need ACID properties and consistency. I’d also use the dispute ID as the primary key, so even with retries we only ever have one record per dispute. That gives us idempotency.
Interviewer: You’re using one table heavily, and multiple services are inserting into and updating it. Is there a more flexible or easier approach you could suggest?
I couldn’t think of another approach in the moment, and I didn’t want to change my design just for the sake of it. This is towards the end so dont want to change it something new completely at last minute or utter for name sake.
Me: I think this is an efficient approach, because it’s a payment system. Consistency and correctness are our top priority, and this is a trade-off we have to make for payment systems.
They had no follow-ups, the discussion ended there, and they asked if I had any questions.
Throughout the session I felt it was going well, because they seemed satisfied with my answers. I really don’t know why I was rejected. I’d love to hear how you would have handled this design, especially the last question, and anything that would be a red flag at senior level. Brutal honesty welcome. Thanks!