Building Strands Statistics and Streaks
A NYT internship retrospective
In the Summer of 2025, I was an intern at NYT Games. During that time, I contributed to pushing out the Strands Stats and Streaks feature. It was a really fun time, and I'm proud of the work I accomplished and think fondly back to working on the team. However, I had no real way of showcasing my work, since it would mostly be linking back to the NYT Games website. So here it is: my own little blog/article on the features I pushed out with the team!
This also serves as my informal intern retrospective and a log of some of the things I accomplished.
Welcome
One of my earliest tasks was to create a dynamic welcome screen for Strands that would display the player's progress on their daily puzzle. A different progress icon and text would be displayed depending on how many words a player has found.
It was super cool to immediately see my changes reflected onto the screen, especially for such a major games feature. However, I would soon learn that coding up a feature was only the beginning!
Feature Flags
Here was my first exposure to feature flags, a crucial part of product development. For every new feature, we wrap the affected code around a flag that acts as a sort of remote boolean (is that a kind of cursed way of describing it?). Feature flags are powerful because they allow for incremental rollout of features. You can more gradually introduce features into your audience base, A/B test the feature, and find bugs during less critical phases of rollout.
As someone who's previous engineering internships were at scrappy startups and student newsrooms, this was quite illuminating. All of my experience was to code fast, break things. Who knew there was a better way of shipping features than just haphazardly dumping it straight into production?
Unit Testing
The next obstacle was unit testing. I was somewhat familiar with unit testing in frontend codebases, however not in any robust way. On smaller, leaner teams this is acceptable - a lot of testing I did came from manual testing and dogfooding. This was relatively okay on a smaller scale, especially with a team that cares about quality while manually testing.
However, consider a larger codebase where many engineers are constantly merging new code. This loosey goosey method of testing is not as reliable anymore, and increases the risk of regressions.
Overall, writing unit tests was relatively straightforward but took some getting used to. Particularly, user state had to be mocked in unique ways, and components had to be mocked and tested for a variety of user states. Our team also used Storybook, which increased the complexity of adding new features but ensured that any version of the component, even conditional UI that is hard to come across in the wild (error screens, uncommon user data) could be tested and verified.
Something clicked for me on the benefits of testing here, where I realized that tests weren't just a sanity check but a form of procedural documentation on how the code is supposed to work. Someone should be able to read your tests and understand how your code is intended to behave.
Ship It!
At last, after completing the feature, wrapping it in a feature flag, writing up tests and then passing the feature through quality assurance I had merged in my first ticket. What an exhilirating feeling it was!
Congrats
Once I got into the flow of completing tasks, the work became more predictable. The rest of what I did follows the same general workflow as I described above.
For the Strands completion screen, I added a display where the player can see a row of their statistics on Strands. This completion screen is dynamic based on the user state of the player. For example, if the player is not registered, instead of displaying statistics, we nudge the user to register an account to see their unique statistics.
The completion screen was especially fun since I got to design my own component! We didn't have an existing component for a placeholder error view, so I got to tackle that myself.
Modal
Next was a dismissable message that notifies players about the new Strands stats feature. The way this component actually works is quite interesting - it uses local storage to ensure the user sees the message at least twice before it disappears.
Both in this internship and my previous experiences I've learned how useful browser storage can be. For simple uses such as this one where we want a lightweight storage per user, we can use browser storage. However, even persistent state such as a user's winning streaks are stored in browser storage.
Browser storage can act as a sort of read and write cache. Consider situations where network conditions fail or user data is not saved properly. Saving to browser storage first gives us a local, almost instant method of tracking a user's progress on a game and deferring database writes when within suboptimal network conditions.
Pipeline
Lastly, was storing and displaying the statistics themselves. Collaborating with other engineers on this task, I learned how we store and track player statistics through a Golang pipeline to a database, incrementing stats such as Spangram First and No Hints when the player enters the Strands player or finishes a puzzle.
While the operations on this database were not particularly complex, reconciling between different player state updates is what introduced complexity to this pipeline - we send a POST request upon entering a game and upon game completion, allowing us to keep track and update player state between devices.
This also means deduplicating any player statistics we want to store. For example, when we receive a POST request and see that the player has successfully completed a puzzle using no hints, we need to verify the previous player state, (that being an unfinished puzzle with no hints) before incrementing the number of puzzles the player has completed without using any hints and displaying it. These edge cases made the process of writing and unit testing player state storage much more interesting than just incrementing a number.
My Secret Second Thing
Under Construction
Retrospective - TL;DR
While listing the features, I was often struck with a feeling of that's it? Not because what happened during my internship was lackluster, but because I realized that I was focusing too largely on the technical aspects and volume, when what was really valuable was the process. When I dug deeper into my experiences, I realized the meat of the real engineering was understanding the impact, the user experience and the systems for managing a rapidly changing codebase.
Largely what I did during my internship was use technologies I've already used in a more refined way. I had this misconception that felt very junior software engineer: that engineering was largely about coding and technical skills. I exited the experience understanding it was so much more - that engineers must refine their product sense and systems understanding.
I found myself quite influenced by the experience - afterwards I would find myself compelled to write engineering docs, draw out systems, and consider source control workflows before I even coded a single line. It made collaborating with people, and even with myself way easier. It definitely helps when you meet and work with so many great people!
Sometimes I still can't believe I contributed to games that millions of people play every day. It's super cool and surreal, and I'm beyond lucky to have had that experience.