My list of predictions about the future of software. I started this list in response to the announcement of Riffle. I have a history of correctly predicting some changes to software development, but I’ve had no way of checking the accuracy of my predictions up to this point, as I did not captured them anywhere. This post will be used to track my predictions, so that their veracity may be verified.
These predictions were written before they came to fruition.
The introductory essay on Riffle was published five days ago, and it appears to be another step toward fulfilling a prediction of mine for web development. Among other things, it’s a reactive database query system, such that your application automatically rerenders when the database is updated. Importantly, the reactivity is derived automatically from SQL queries. When you perform a query in the UI, the system will subscribe to changes to the resultant data and rerender your UI when that data is altered. Granted, Riffle is “local-first,” which means it’s not much different from Redux in its utility, but the important step forward is that they are automatically deriving observation from basic SQL queries. I predict that someone will build a reactive SQL system for cloud databases, and it will take over web app development. It is also possible that someone will invent an entirely new database with reactive queries, or that an existing database, like GraphQL, will be extended to include them.
A reactive query system will work as follows. The app developer will use conventional queries to access data, not subscriptions, during a render step. The reactive system will then parse the query to determine which subscription to the database is required such that any change to the results of the queries triggers a notification to the client. That subscription will automatically be applied at the same time that the database is queried for the current state of the requested data. The UI will then be rendered to the screen, but every time that the database changes in such a way that the results of the query might have changed, the query will be rerun, and any changes will trigger an update to the UI. The application code will then consist of a mapping between database queries and UI elements, with no code dedicated to storing the results of a SQL query, subscribing to changes, or reacting to changes specifically.
The complexity of building this system comes from the delay in accessing or updating data on the web. Riffle, in particular, uses a local SQLite database running with WASM, which makes automatic updates almost instantaneous. In contrast to a reactive system, imperatively handling state allows an app developer to decide how they want to handle local caching of or asynchronous updates to the database. Roughly speaking, a reactive query to the cloud would need to handle the delay in communication in one of two ways, either with loading screens or with a predictive cache.
To introduce loading screens, a loading status would be returned whenever the query is yet to be fulfilled or the local cache of the remote database determines that the query will likely change due to some pending update. If a reactive query returns a loading
status, the UI code could then handle that status however it chooses to, likely with a loading screen of some form. A loading screen may appear to diminish the utility of a reactive system, but the advantage lies in not having to imperatively manage subscriptions; not in avoiding any appearance of asynchrony.
The second way to handle the delay in communication is with a predictive cache of the remote database. A predictive cache would update immediately whenever a change request is transmitted to the remote server. The UI could then be rendered synchronously, which would work perfectly if the network couldn’t fail, which, of course, it can. If a network failure occurs, a reactive system would have the tools to roll back the UI to match the new understanding of the unaltered database. Since the reactive system controls UI updates and state changes, the system could simply rerender with the prior database state. The system would then have two choices. It could roll back all local state to fix any erroneous updates since those updates were based on the false notion that the prior change was successful, or it could keep the local changes to navigation and have the update silently disappear. I doubt either of these solutions would be widely adopted, as it would either manifest as the application skipping backward without warning, or data disappearing from the app without warning, neither of which would result in an acceptable user experience.
An alternative method to handling a failed update is to simply notify the application UI that the network is unavailable and that the local cache is not being persisted to the cloud. This would allow the UI to display a notification or to halt further interaction by the user, which is how most web apps work today. This solution would likely be acceptable to most developers, and so, if a predictive cache is used, this is the solution to network failure that I predict would be implemented alongside it. The status of the network would then be provided using an alternative query, which would also be reactive. The query could be configured based on tolerances for network delays, like “has the network been down for more than three seconds.” If the answer to such query is “yes, the network has been down for more than three seconds,” the render function would branch into its notification renderer, or a blocking error screen, whichever the developer prefers. It should be noted that Riffle appears to be compatible with many of these solutions, as a local database does not preclude background syncing.
I don’t believe that LLMs will actually take over software development in the next decade. I do believe that they will become much more integrated into our process, but I think the companies that are currently relying heavily on AI to replace their software developers will find that the AI is incapable of producing quality software when a feature request is even slightly novel, resulting in low-quality, buggy, unmaintainable code.
Warning, I’m going to shoot for the moon on this prediction. We’ll see how close I get.
LLMs use in-context learning to produce code, and lack the reasoning skills necessary to take over programming. In particular, LLMs are bad at designing data-structures, which has a large influence over the architecture of the code that has to operate on that data. Since LLMs base new code off of old code, a poorly architected code base will only become worse as the AI continues to write code in the system.
At the moment, programmers have to describe and review all code changes written by LLMs, but as time goes on, software development teams will start to realize that the code produced by an LLM is difficult to describe changes for. Eventually developers will start writing post-mortems about their unmaintainable, AI-led legacy software, at which point the fervor over LLM programmers will become tepid. In the end, the role of LLMs will be scaled back to suppliment people’s work rather than trying to replace them. The tools will be redesigned to operate more as a combination of a highly sophisticated autocomplete and a static analysis tool, warning you if the LLM detects abnormalities or potential bugs. LLMs will still be prompted to make changes, but only when the developer deems that the LLM is well suited for the change.
The worst software developers may lose work, but the best software developers will only become more valuable, as they become more effective. In addition, LLMs will ultimately make the job of programming harder, rather than easier, as the programmer will have to spend more time on the difficult tasks that the LLM was incapable of handling.
To go even further, I do believe that we will see a shortage of senior developers due to the loss of work for junior developers. This will result in a slight shortage and may result in a shift toward apprenticeships for new software developers. Seniors will become masters who teach the junior to replace them. This will become necessary as there are no longer any simple tasks for the junior developer to take on and with which to gain experience. Instead, companies will expect the junior to move directly to more complex work, which may be best achieved through an apprenticeship under an already established coder.