Anyone could set their own sales target, and half the screen did nothing
The brief was mild. A sales organisation said the screen where managers set monthly targets “is not user friendly at all”, and asked whether we could improve it.
We went to look at why before redesigning anything, which is how a cosmetic complaint turned into three findings that made every performance number in the business questionable.
Anyone signed in could write anyone’s target
The endpoint that saves a target had no permission check and no role check. It read the caller’s role from their login token and then never used it.
Any authenticated account — any sales rep, on their own phone — could write a target for any employee in the company, including themselves. Nothing in the system would have recorded it as unusual, because as far as the system was concerned it was a normal save.
Consider what rests on that row. Performance reviews. Incentive calculations. The dashboard a regional head reads on a Monday. All of it measured against a number anybody could set, and set to anything.
A second endpoint reporting revenue had no guard on it either.
We fixed it and then proved the fix on a real environment rather than asserting it: not signed in returns 401, signed in as a sales rep returns 403, an administrator returns 200 — while each rep can still read their own performance, because that route is scoped by their own identity rather than by a permission.
Half the screen had never worked
The reason the screen felt unfriendly was more literal than anyone had assumed.
The front end had been calling four endpoints — list, fetch one, update, delete — since the day it was written. None of them existed on the server. The grid, the edit button and the delete button had never done anything. Only the spreadsheet import and export worked, which is why everybody used the spreadsheet.
Nobody had reported it as broken. Users had been routing around it for so long that the dead half of the screen had become part of the furniture, and it surfaced as a complaint about friendliness.
That is worth noticing as a pattern. Software that quietly does nothing rarely gets reported as broken. It gets reported as awkward, or not at all, and the people using it invent a workaround and stop mentioning it.
The numbers were in units nobody had agreed on
Then we looked at the data already in the table. 866 of 877 live target rows were under ₹1,000 — values like 10, 5, 1, 8, 15. People had been typing crores into a column that stores rupees. A further 187 rows were literally zero.
So even where a target existed and was set by the right person, the figure in it could be out by seven orders of magnitude, and every roll-up silently summed the mixture.
Both write paths now refuse a value between zero and a thousand and explain why, and the field warns before you can save. That is a crude rule and it is the right one: no real monthly target is ₹15, and a validation that reflects the business is worth more than one that reflects the column type.
Six more, found by using it
We then used the rebuilt screen as a person would, which produced six defects that reading the code did not. The one worth repeating:
The uniqueness rule that was supposed to stop a person having two targets for one month was applied to a timestamp rather than to the month. So a row saved at midnight UTC and a row saved at midnight Indian time were, to the database, two different months — both allowed in, and both summed by every report, which groups by Indian months. A person with both showed double their target.
We found it by noticing a figure reading ₹10 lakh where ₹5 lakh had been set. It is now indexed on the month itself.
One of the six was caused by our own change during this work, and the client found it before we did. It is in the count because a list of defects that only contains other people’s is not a list anyone should trust.
What the screen is now
The depot roster is the form. Every salesperson appears as a row with their current target pre-filled, with search and a filter for people who have none. A “reports to” column names each person’s leader and what that leader is carrying, flagged in amber when the leader has no target — which is the case that makes a subordinate’s number arbitrary, and the case nobody could previously see.
The manager is resolved in the database rather than read off the visible list, because a state head usually sits in a different location from the people reporting to them and would not be on the screen at all.
What we would take from it
The request was to make a screen nicer. The screen was the least of it.
If your organisation measures people against stored numbers, three questions are worth asking this week, and none of them need a developer to answer:
Who is technically able to write that number — not who is supposed to, but who can? Has anyone opened the table and checked the values look like money? And when a user says a screen is awkward, has anybody sat with them while they use it, or was the complaint filed and scheduled?
The answers here were “everyone”, “no”, and “no”.
Findings like this one usually surface while writing down what a system does precisely enough to be useful — which is the first step of the work described under AI data assistants.