There is a file in Notifio with six constants in it and a header comment that exists to stop me turning any of them into a setting.
/**
* Fixed auto-reply behaviour.
*
* These are not settings. They are safety rails that protect the user's accounts
* on the sites they are replying to, and a sensible person has no way to pick
* good values for them. Exposing them as a form of six numbers made the feature
* feel like configuration work instead of something that just runs.
*/
The feature they govern sends the first enquiry on a new rental listing for you, on the portal, from your own signed in account. The second sentence of that comment is the load bearing one. Every value here is a decision about someone else's account standing on a third party site, and the user has no information with which to choose it. Asking them to is not flexibility, it is handing them a liability with a number field attached.
The three that are rate limits
/** Replies across all sites in a rolling 24 hours. */
export const DAILY_LIMIT = 50;
/** Replies to one site in a rolling hour. */
export const PER_SITE_HOURLY_LIMIT = 10;
/** Replies to one site in a rolling 24 hours. */
export const PER_SITE_DAILY_LIMIT = 30;
Three windows rather than one, because the failure modes are different. A global daily cap is about you: fifty enquiries in a day is already more than any real search needs, and a number well past that usually means something has gone wrong in a loop rather than that you got very unlucky with the market. The per site caps are about the portal: a hundred messages into one site in an afternoon is a pattern no individual house hunter produces, and the account it comes from is yours, not mine.
The counts come out of the append only reply ledger I described in 21 reply statuses, and the 8 that mean never touch this listing again. Worth repeating one detail from it here: a reply that failed does not consume quota, but it does permanently block that listing. The two questions are answered from the same rows with different status sets on purpose.
The two that are a delay, and cost more than they look
/**
* Randomised pause before replying, in seconds.
*
* The pause is also the bulk of a reply's runtime, and replies run inside the
* poll cycle: nothing is scraped while one is waiting. A tighter window means
* both a faster reply and less time blind.
*/
export const MIN_DELAY_SECONDS = 10;
export const MAX_DELAY_SECONDS = 30;
This is the constant I expected to be least interesting and it turned out to have the sharpest trade in the file.
The pause is there because replying in the same second a listing appears is not a thing a person does. But the reply runs inside the same poll cycle that checks your searches, and while a reply is sleeping, nothing else is being checked. A minute of pause is a minute in which the next room to be posted goes unnoticed.
So the window is pulled tight from both ends by the same argument. Too short and the reply looks scripted. Too long and the product stops doing its actual job, which is being early. Ten to thirty seconds is where those two pressures meet, and the comment says so, because a future me looking at "why is this only 30 seconds" will otherwise conclude the obvious fix is to raise it.
The one that ticks a box for you
/**
* Terms and privacy checkboxes are ticked on the user's behalf.
*
* Most contact forms will not submit without one, so making it optional meant
* the feature silently did nothing on a lot of sites. Marketing opt-ins are
* never ticked, and every reply records which fields were filled.
*/
export const ACCEPT_REQUIRED_CONSENT = true;
This is the only one I went back and forth on for days.
Ticking a terms box on behalf of a user is uncomfortable to write. The argument that settled it: the user is not being surprised by a contract, they are sending an enquiry they explicitly set up, on a site they are already registered with, having already accepted those same terms when they made the account. The checkbox on the enquiry form is a restatement, and a form that will not submit without it is a form the feature cannot use at all.
Marketing opt ins are a different thing entirely and are never touched, even when they are pre ticked, and even when leaving them alone means an extra click for nobody. The distinction is drawn in the form reader rather than at reply time, with separate patterns for consent wording and marketing wording across six languages, so a box has to actually be a required legal acknowledgement to qualify.
And every reply records which fields it filled, so the answer to "did it tick something for me" is a fact on disk rather than a promise in a blog post.
The one that is a sentence, not a number
/** Off-domain replies are refused by design. Not configurable, ever. */
export const ALLOW_OFF_DOMAIN = false;
It exists as a constant so that the guard code reads as a policy rather than as a hardcoded false somebody could reasonably delete. I wrote about the domain lock itself in €1450 per month is rent, €9.99 per month is a paywall.
Deleting settings is a repeatable move
This is the second round of deletion in the same feature. The first took out the profile store, then message templates, then the shared message, and left a settings file with one field in it. Those went because a recording already contained everything the site asked for. These never became settings in the first place, because there is no version of the user who benefits from choosing them.
The test I now apply before adding any option: can I write the sentence that tells the user how to pick a good value? For "how many messages a day should go to one rental portal from your account", I cannot. Nobody can. So it is a constant, and if it ever needs to change, that is my decision to make and ship.
The feature is on notifio.app/pricing, the setup flow is in notifio.app/help, and if you want the product argument for why being early matters this much, how fast do rental listings go is the page that makes it.
Top comments (0)