TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Ask HN: How to write code without having to refactor

3 pointsby valtyover 1 year ago
Say I&#x27;m writing a handler for an http server.<p><pre><code> function handleRequest(req) const url = new URL(req.url) if (url.pathname === &#x27;&#x2F;foo&#x27;) { &#x2F;&#x2F; do a thing } if (url.pathname === &#x27;&#x2F;bar&#x27;) { &#x2F;&#x2F; do a thing } if (url.pathname == &#x27;&#x2F;routes&#x27;) { &#x2F;&#x2F; should print &#x2F;bar and &#x2F;foo } } </code></pre> The `&#x2F;routes` endpoint should print information about the other routes.<p>The problem I have is that the code to handle the endpoints is the simplest code to do what I want. It is what would come naturally to most developers.<p>But as requirements change, like to list the routes, I find that I need to refactor everything dramatically just to add one small additional feature of printing out my routes.<p>For example, the obvious thing most people would do is put all the routes in an array. That is, my routes now become objects. But if I have a more complicated conditional to route match instead of just `url.pathname`, then things start becoming way more complicated.<p>I find this situation often when coding, and I find it creates so much unnecessary complexity.<p>Is there a better way to code to prevent having to constantly refactor code?<p>What I am thinking about at present is code reflection. The simplest way to do what I want here would be to query the code AST for all conditionals in this function, and print them out.

4 comments

HelloNurseover 1 year ago
Your code example, I assume it&#x27;s Javascript, is as unstructured as possible. The real world is less trivial.<p>For example, if you need &quot;&#x2F;routes&quot; to list all routes you MUST have a map of routes to an object or function describing what to answer for that route, so that the code for &quot;&#x2F;routes&quot; can inspect it and work without duplicating information.<p>Then populating and passing around this configuration object becomes the core business of your request dispatching framework, along with fixed, centralised behaviours (e.g. testing authentication and answering 403 instead of serving the regular document if not passed) and reusable mechanisms (e.g. extracting HTTP headers and request parameters into nice data structures and making them available to request handlers).<p>Can&#x27;t you use a mature existing library?
ssss11over 1 year ago
Real world code is messy. You will refactor, unless you’ve already considered every possible function you’ll code into the app in the future.
KingOfCodersover 1 year ago
The only way to not refactor is being able to look into the future. And then why code and not play the lottery.
Hackbratenover 1 year ago
First things first: I think the way you implemented it is really simple and easy to understand at a glance. There’s basically nothing significant to take away from it. It’s perfectly good code as it stands, before the “list all routes” requirement came up.<p>Before you knew you’d want to list routes, any abstraction would have been premature, and unlikely to be helpful because it would have possibly been the wrong abstraction. Chances are you’d have had to redo it anyway the moment your “list all routes” requirement arrived.<p>&gt; The simplest way to do what I want here would be to query the code AST for all conditionals in this function, and print them out.<p>Yes and no.<p>Simplicity is not a one-dimensional metric. You’d keep the simplicity of your switch statement, but introduce the complexity that is AST parsing. You’d also introduce action-at-a-distance. What if another developer decides to change `(url.pathname === &#x27;&#x2F;bar&#x27;)` into `(url.pathname ~== &#x2F;bar\&#x2F;(?&lt;subpath&gt;baz|qux))`, unaware they’re going to break the assumptions of your AST-based listing feature?<p>There are several (equally simple, or even simpler) methods to implement your requirement other than resorting to AST parsing.<p>But what actually makes a good solution is likely to hinge on particular details, context, and requirements of your project. For example, I used to have a similar requirement in a Java project a few years ago. I wanted us to be able to enumerate our API endpoints at development time, and generate a JSON schema of a specific endpoint for reference. What I ended up doing is introduce a custom annotation, and write a plug-in for our build system that added a `gradle api:listEndpoints` task and a set of `gradle api:generateSchemaForFoo` tasks that integrated well with our IDE and could be used from the command line, too. In retrospect, I think my approach was perfectly fine for the specific situation we tried to tackle. However, that approach would likely have been a poor, hopelessly overengineered solution for other contexts.<p>With all that out of the way:<p>&gt; That is, my routes now become objects. But if I have a more complicated conditional to route match instead of just `url.pathname`, then things start becoming way more complicated.<p>&gt; I find this situation often when coding, and I find it creates so much unnecessary complexity.<p>Maintaining a list of route objects doesn’t have to be complex.<p>I know you didn’t ask for implementation advice, but here’s one anyway just to illustrate my point (untested):<p><pre><code> const routes = { &quot;&#x2F;foo&quot;: handler.doFoo, &quot;&#x2F;bar&quot;: handler.doBar, &quot;myComplexCondition&quot;: { description: &quot;URL path that includes the magic word&quot;, matches: (url) =&gt; url.pathname.includes(&quot;xyzzy&quot;), handle: handler.doXyzzy, }, &quot;&#x2F;routes&quot;: handler.printAllRoutes, }; function handleRequest(req) { for (const [id, routeExpression] of Object.entries(routes)) { const routeConfig = { id: id, description: id, matches: (url) =&gt; url.pathname === id, handle: () =&gt; { throw new Error(&quot;Missing `handle`&quot;); }, ...( routeExpression instanceof &quot;string&quot; ? { handle: routeExpression } : routeExpression ), }; const url = new URL(req.url); if (routeConfig.matches(url)) { return routeConfig.handle(url); } } throw new Error(&quot;No route matches&quot;); }</code></pre>