Oct 4, 2022
When developing simple client-side React applications, one often needs a touch
of backend services. Not an entire database (with its management &
administration needs) or high performance compute. These services have their own
use cases, but for simpler requirements, they often take more energy to set up
than necessary. It’s also difficult to scale these services up or down depending
on usage, which might lead to unnecessary overhead (when you don’t utilise them
fully) or service interruptions (due to traffic spikes).
Enters the notion of serverless infrastructure – a paradigm of providing
backend services to the front-end on a as-used basis. Serverless vendors charge
usage, not bandwidth and hence solve two problems with one solution – that of
boosting (cost) efficiency and scaling up on demand. This creates a refuge where
application developers can write and deploy code without worrying about
infrastructure needs. Like all refuges however, there are trade-offs in
inhabiting this system. For the usecase detailed in this post, serverless
computing suffices and I’ll outline how I used Cloudflare’s edge network as a
serverless platform.
When I first started this project, I wanted to build a live chat application,
one of the fundamental pieces of software in any social product. I made an
app using the popular Javascript
express (server) and socket.io
(websocket) libraries which worked fine in local development. When it came to
deploying it online, I didn’t want to just deploy it openly (without
authentication) as there might be bots crawling the internet, looking for open
websocket servers to spam and so on. On the other hand, I didn’t want to use my
domain for authentication (and registration) services for this pet project. The
idea of using serverless JS workers came to me as I was thinking about how to
architect the application with the least possible backend exposure. Luckily,
several cloud vendors provide such services nowadays so it’s only a matter of
time to integrate with their interfaces.

Cloudflare workers
Cloudflare workers are JS modules / functions running on Cloudflare’s global
network in over 200 cities around the world. Cloudflare’s reputation of
providing DDOS protection services implies that they have spent time optimizing
resilient cloud networks and CDN services, and they offer to front web traffic
for other businesses. So in essence, a cloudflare worker is a reliable online JS
function which is run on CF’s network servers anytime you call the function.
This is great for non-persistent (light) compute, but the workers platform also
includes
simplified key-value storage and consistent distributed storage via Durable
Objects. Of interest to us is this last one, namely Durable Objects. We
want to implement a chat application (with it’s UI layer written in React) over
a global distributed network.
Suppose there are five participants in different parts of the world, talking in
a group chat. To provide real-time feedback, one needs low-latency coordination
among the servers closest to these users and even more importantly, the messages
in the group chat have to be in one order for all users (a fairly difficult
problem). See how quickly the problem of enforcing consistency over distributed
systems becomes demanding? Cloudflare’s Durable Objects try to tackle this by
implementing a locking mechanism to enforce Global Uniqueness and using a
transactional single-threaded storage API while reading / modifying key-value
pairs. This suffices for a wide variety of use-cases (certainly ours in this
blog post), but you can also think of use-cases where you might need more local
(& better) consistency resolution (like in one particular geographical region).
Our
worker
implements a fetch
listener (wrapped in an error handler) and passes on API
requests further, to instantiate a ChatRoom class. The instance of this class
runs as a Durable Object and creates chatrooms while handling sessions to this
chatroom. Since we don’t want to persist data, we don’t store it anywhere and
all data is lost as soon as the websockets enabling the sessions are
disconnected / closed. The ChatRoom Durable Object also instantiates a
RateLimiter class to ensure worker compute is not abused by heavy traffic, but
is rather moderated via rate limiting.
Passing callback functions to React’s useState
One of the issues I came across while implementing the UI in React was to
re-render the components upon receiving messages through the web socket. React
components usually do not re-render until some state (which is being accessed in
the component) is updated. This is, in general, a strength of React as it promotes performant rendering while defering expensive operations – namely, re-render only when necessary. So when sometimes there is no explicit need of state in a component, external triggers of rendering are carried out by dummy state, like in this instance, a dummy msgCount
state number.
Another issue I faced was sometimes encountering incoherent state updates (and
renders) in my App component. When using the useState
hook, the setter
function (some form of setState
) is used to update local state and ask React
to re-render the component (update the DOM). Usually, one often just calls
setState
with a new value passed in as an object, for e.g.,
setState(newState)
. What if our newState
depends on the current state
variable? Directly accessing state
is not a reliable way to cause this
update as React handles state updates asynchronously. This doesn’t show easily
in all applications (for instance, when some other state variable being updated
pushes this state variable to also be updated and saves our application from
breaking) or when the state update is a rather isolated low-cost operation
(which somehow escaped React’s batching of operations for the next render). To
quote React’s
documentation,
“Because this.props
and this.state
may be updated asynchronously, you should
not rely on their values for calculating the next state.” Now I usually don’t
face these failed “renders”, but in this app, frequent websocket messages (which
often happen in fast succession) “failed to re-render the UI” and any further UI
updates almost “missed out” the state updates before (for e.g., if you’re
appending a messages
list with messages flowing from a websocket). At first, I
tried to fix this by adding more dummy state variables (without success) and
using useEffect
and useRef
hooks to force more re-renders, before finally
realizing the main problem. What I wanted was not more renders, but more
reliable state updates, as the excess renders also kept showing the old state,
although sometimes incoherently. It wasn’t the renders that were failing, but the state updates.
The conclusion is to pass callback functions to setState
hooks when one needs
the current state or props to calculate the next state. React is then able to
reliably queue up these “pipes” / transformations to apply to the current state,
without relying on what the current state actually is. This is almost so
important that there should be an ESLint plugin for this (and there is
one).
This app has been pretty useful to me to acknowledge the subtleties of React’s
useState
hook (and rendering mechanism) and to understand how capable
serverless infrastructure is. Try out the
app, and let me know your thoughts!
This article was cross-posted to Medium & Dev.to, so you can also follow there
or post comments.Sep 17, 2022
React is today one of the most popular frameworks for coding UI layers of modern
web apps. By the moniker “modern”, I mean an era where best practices have
matured for the vast majority of use cases & some level of declarative
programming has been achieved (you code what should happen, instead of how to
do it).
I think there are two fundamental aspects of React’s popularity (especially
compared to the frameworks / libraries used before it):
Component architecture. Stemming from the CS notion of separation of
concerns, React simplifies building complex apps by encouraging modularity
(plug-&-play), reusability (DRY) and clear abstraction. This aspect has been
kept in mind in the former frameworks as well, and React continues to implement
it (much to the benefit of engineers for whom React is their first framework).
Note the emphasis of React’s separating concerns despite coupling HTML/CSS and
JS.
Reactive programming. This is a declarative paradigm to create systems
where asynchronous functions automatically handle updates to static content,
without the programmer having to explicitly implement the updates. Over the last
decade, this has transformed the engineering world with the rise of the internet
(aka asynchronous land). Event loops/queues & efficient updating of
declarative code has been extensively used to design reactive
systems and React is a library bringing
similar patterns to web UIs. Like in much of engineering, while imagining a
system where only the updated UI pieces (only things to be done) are re-rendered
(get done) is easy, implementing it in reality is hard (browser DOM operations
are expensive & not designed for it). React tackles this by using a
reconciliation algorithm on a virtual DOM (a JS object representing the DOM
tree) to handle this reactively.
Chess transcriber
Here I want to talk about how I implemented a chess
transcriber using React
(source).
Designing component architecture. The UI has a decent level of complex logic
to it. At first, I divided the app in a Left Sidebar, main Chessboard and the
Right Sidebar. The Left Sidebar should state instructions on how to use the app
& let you choose functionality, the Right Sidebar should give
functionality-specific instructions. And the main Chessboard is where all action
should happen.
Implementing the Chessboard took some thought and really capitalizes on the
abstraction model of React. The entire Board (enclosed in a wooden box) has 64
Tiles, where each Tile’s props characterize it’s color, an onClick event
listener, the piece on it & options to highlight it. I used
the chess.js library to handle the
underlying chess logic (the library has been coded over 10 years and provides an
excellent API), and designed the UI dataflow keeping the library’s interface in
mind.
Handling the application state. At first, I tried passing props around to
handle the UI updates, but it quickly became too complex to deal with. Not that
it couldn’t be done, but the resulting design was ugly (not well abstracted,
lots of re-implementation of code & lots of passing data to places where it
doesn’t exactly need to be). I researched a bit, and indeed there are central
state store implementations (often with the Flux architecture), which people
like to use for complex component trees. I chose Redux to store where the pieces
exist on the board & which are the highlighted tiles for a move, etc., and made
the app work.

A second look & re-architecture
The more I learnt to think in React (and since I wanted to improve the app), I
thought through exactly which component needed which data flow and how to
arrange them in a well-organized dataflow-lean way. I decided to re-architect
the app making the following changes.
Component architecture. At first, I had divided the components focusing on UI,
but that seems like a mistake. I have now divided the components based on
functionality. The UI is an implementation / representation of the
underlying UI logic — the main app (in JS) which says what is happening. This
app (like any application code solving business logic) is divided
into functions (i.e., by functionality). It often happens that this
translates to separate UI components, but it’s important to note that the
underlying driver of this abstraction is functionality, not UI. (For example,
you can have a UI mixing up all outputs of various components in a well-written
React app).
State management. I realized that my app (much like a lot of apps out there)
doesn’t really need to use Redux or even React’s Context API. This came in
time when
I realized that using Redux really makes sense when you want the actions (state
changes) themselves (whether for bug reports, persistent storage or syncing UI
across network). That is, in a way, Redux helps to decouple the actions from the
components themselves. Even deep component trees which on first glance seem like
they would benefit from a central store (via Flux or Context) can often be
rearranged in a way that encourage component composition with
appropriate local state initialization / usage.
This app has been pretty useful to appreciate the underlying design choices of
React and to understand when exactly to use (or not) Store libraries. Clean code
comes from clean architecture, which requires having a clear idea of
what exactly various tools offer, and when they make sense to use. Try out the
app, and let me know your thoughts!
This article was cross-posted to Medium & Dev.to, so you can also follow there
or post comments.Sep 5, 2022
One of the first things beginners in software engineering learn is to dote on
their source code. As if getting up to speed with the latest tech stacks &
platforms was not challenging enough, the industry (not without reason) places
great emphasis on how you code. One hears about phrases like human centric
design & testability, and wonders what the problem is if one’s software just
works.
Why can’t the thing just work?
One needs to undergo a mindset shift to realize that code must be written not
only to make the end product work, but also to be readable to other developers.
That is, of course, if one does not wish to solo their way through the entire
project lifecycle (not a great idea for anything long term).
So the idea to internalize is that your source code is also a product. That’s
the product you use to demonstrate your skills (sell) to your future
employers/collaborators, with whom you then collectively sell your software to
users. Once you have this mental model set up, it’s much easier to make sense of
software engineering culture and **understand where the vast amount of
literature (blog posts, video, podcasts, etc) comes from.
Social trumps all
Human beings tend to get social about the things they do, no matter how
technical or mathematical they are. That’s called Conway’s
law or something. Namely that
organizations design systems that mirror their own communication structure. What
happens if you apply this law to an industry? See what I mean?
People generally also care more about the social aspect of their work than just
technical correctness, or even mastery. That’s why opinions, conventions and
standards start to matter. Developers have tastes and it’s easier to work
together when those tastes are in tune. That’s one reason (among others) why you
hear empathy talked about so much in developer circles.
Organizational thinking
There’s another important aspect. Any large influential project or organization
must necessarily involve many people. While it may naively seem that multiplying
effort multiplies output, it takes great foresight & management skills to make
one plus one greater than two.
Behaving oneself
Most of the non-programming skills engineers need to master are just that.
Project management skills in the context of software. While much of this is
commonly learnt like gospel, in a “this is how you do it” sort of way, there’s
value in sticking to the basics and realizing that professional engineering is a
social process, and there are notions of manners. While many of these manners
aim to improve productivity / simplify architecture, some of them have
idiosyncratic origins. And that’s ok. Just important to distinguish them from
the technical skills, to understand the motivations behind workflow choices.
This article was cross-posted to Medium & Dev.to, so you can also follow there
or post comments.