All posts by bponnaluri@gmail.com

Slowing down coding speed can be beneficial

Earlier this week, I decided to try using Vim instead of an IDE for working on createthirdplaces.org. My coding speed has slowed down significantly as I am not used to Vim. I think the slowdown is somewhat of a benefit right now.

  • Right now, I am prioritizing user experience by displaying pages in a way that makes sense for users, and making sure information is organized in an intuitive way. As part of this process, I’m thinking about the user feedback I have received recently. Slowing down is helping me think carefully about the changes I am making to ensure that they are addressing feedback.
  • Slowing down makes it less likely that I am producing too much content. Too much visible content overwhelms users. If I can improve the user content by making it less visible, that means users are unlikely to see the content, and working on that content probably did not need to be a priority.

Automating human communication will probably make customer service worse

I often hear that automating human communication in ways such as adding self checkout lines, an online chatbot, or an automated phone answering system improve customer service. I think the opposite is usually the case.

Companies which automate customer interactions have a variety of goals besides customer service, especially making a profit. Humans in customer service roles will have empathy for the customer, regardless of what their company training says. Companies have far more control over how a customer service bot is trained, and can design them to make profits a priority, without any empathy for customers. Customer service bots can also be used to giving the appearance of providing customer service, without having to pay a human.

Using a automated ordering system at a restaurant instead of talking to a cashier or having to use a customer service bot feels very depersonalizing. I’m also reminded of people who lost their jobs, while the cost of food, healthcare, and housing is going up. I prefer the inefficiency of not having automated checkout systems or customer service bots. Also, I think adopting more pro-human values is a necessity as technology advantages.

While I think it would be ideal to not automate customer service, sometimes automation of customer service can be a necessity. A small business such as a local coffee shop may not be able to earn enough to pay for fully human customer service. There are various overhead costs such as buying food, and paying rent.

Between 1983 and December 2025 overall prices in US urban areas rose by 326.03%. On the other hand the cost of food in urban areas rose by 344.693%. The cost of shelter rose by 420.983%, which means customer service need more money, and high housing costs does correlate with high rents for commercial establishments. With restaurant margins between 0-15%, even relatively small cost increases can cause a restaurant to go out of business or automate customer service to save money. On the other hand, the cost of IT hardware and services has gone down by 93.292% since 1988, and this is a key reason why automation increases profits.

Due to the relative cost of human customer service compared to automation, trying to convince people to prioritize human customer service over automation using an efficiency argument is not going to work. I think it’s better to focus on saying human customer service is better because, customers will get a better experience, automation related job disruption will be reduced, and we promote more prosocial norms that help us effectively manage new technology.

To summarize, automation will probably make customer service worse, and we should promote more prosocial norms to encourage human customer service.

Grocery stores and self checkout

As an example, I prefer getting groceries at Trader Joe’s over Safeway. One reason is the fact that Trader Joe’s locations I’ve been to do not have any self checkout counters. On the other hand, the Safeway near me has self checkout, and sometimes self checkout is my only option. Ratings of the Trader Joe’s near me are also significantly higher than the ones for Safeway, which is a sign that people see Trader Joe’s as a grocery store that provides better customer service.

I’ve also noticed that employees at Trader Joe’s also appear to be happier and provide better customer service. Employees at Safeway tend to be unhappy and checked out. To me, this is a sign that Trader Joe’s treats it’s employees better. Trader Joe’s having better employee reviews on Glassdoor and Indeed is another sign. I don’t think it’s a coincidence that all of the Trader Joe’s I’ve been to employ human cashiers and do not use self checkout.

Not using checkout is also representative of management’s concern for customers and employees. I think one significant cause of the fact that Trader Joe’s treats it’s employees better and has better customer service is the humanizing effects of having people work as cashiers. Also, it means that management made a decision to not use self checkout. On the other hand, Safeway’s management deciding to use self-checkout does show a lack of concern for customers and employees.

On the other hand, prices at Safeway are significantly lower. As a result, I still buy groceries at Safeway, despite the fact that I do not want to use self checkout. Lower prices is probably also a reason why other people buy groceries at Safeway.

Here is a comparison of prices for some food items at Safeway and Trader Joe’s as of January 2026.

FoodTrader Joe’sSafeway
1 banana$0.23 $0.25
12 eggs$5.99$4.99
Milk carton(64 Fl Oz)$5.99$2.99
Canned tomatoes$1.49$1.99

https://www.glassdoor.com/Overview/Working-at-Safeway-EI_IE581.11,18.htm

https://www.glassdoor.com/Overview/Working-at-Trader-Joe-s-EI_IE5631.11,23.htm

https://www.traderjoes.com/home/search?q=&global=yes

https://www.safeway.com/shop/search-results.html?q=12%20eggs&tab=products

https://www.indeed.com/cmp/Trader-Joe%27s/jobs

Average reviews for nearest Trader Joe’s and Safeway to my place.

WebsiteTrader Joe’s ratingSafeway rating
Google Maps4.6 stars3.6 stars
Yelp4.2 stars2.3 stars

Sources on inflation and restaurant profit margins

https://fred.stlouisfed.org/series/CPIAUCSL

https://fred.stlouisfed.org/series/CPIUFDSL

https://fred.stlouisfed.org/series/CUUR0000SEEE


https://fred.stlouisfed.org/series/CUSR0000SAH1

https://merchants.doordash.com/en-us/blog/profit-margins-restaurant-businesses

https://www.restaurant365.com/blog/what-is-the-average-profit-margin-for-a-restaurant-2

https://pos.toasttab.com/blog/on-the-line/average-restaurant-profit-margin?srsltid=AfmBOooSbQLw0lcXziUbdBoE0D7dmzcGcOK4wQuoY1_vOthfxn4acTTF

Here are some links I think are relevant

https://en.wikipedia.org/wiki/Baumol_effect#

https://www.unesco.org/en/articles/baumols-cost-disease-long-term-economic-implications-where-machines-cannot-replace-humans

https://www.strongtowns.org/journal/2026-1-12-christmas-cookie-inflation-index-2025-update

Improved technology is going to require more human centered values and communication

As technology advances, the idea that we can use technology to replace human interaction is misguided. This is not related to how advanced technology becomes, and I think it will be true, even in a theoretical world where AIs were smarter than humans.

As technology becomes more advanced, the scale of it’s impact will be larger, and we need to make sure it is used for constructive purposes instead of destruction. This requires humans to work together and develop social norms around how technology is used. For example, nuclear fission was used in the 1940s to develop nuclear weapons, which have become powerful enough to turn our planet into a lifeless wasteland. However, there is also a collective understating that nuclear weapons should not be used in war, and nuclear fission is now used in power plants to generate electricity. While nuclear power plants are complicated and generate radioactive waste, they are also far more energy efficient and generate less air pollution than coal plants.

When it comes to communication, technologies that facilitate communication when it otherwise isn’t possible have made the most positive impact. Being able to make a phone call or send an email to someone who isn’t physically in the same room as you has been very beneficial for society.

Sometimes, promoting human centered values is going to require avoiding use of a technology, even if the technology makes work more efficient. For example, AI art generators can generate art far more efficiently than a human artist. However, AI art generators lack empathy and the ability to connect with an audience, so using them moves us away from human centered values that are becoming increasingly important. AI art generators are also trained on the work of artists without permission, financial compensation,or credit, which is anti-human. In other words, we should be supporting human artists.

Also, technology is developed and maintained by humans that have to communicate and work together. Nobody is able to develop new technology entirely on their own with no help. First of all, you need to talk to potential users to see if what you are making will solve a problem they care about. Once the technology is developed and proven to be useful, you then need to convince people to use the technology. Then, you will need user feedback about bugs, new features, and places where the user experience needs to be updated. Successful technologies such as electronic computers are rarely created in isolation, released to users, and then never modified again.

One more reason task-specific models are the future of AI, and LLMs are not

Researchers at Goodfire AI isolated memorization from problem solving in large language model(LLM) neural networks.

I think this is additional evidence of the fact that LLMs are very inefficient in a manner similar to blockchain. Blockchain was capable of solving real world problems by wasting large amounts of energy. However, the inefficiencies of blockchain weren’t being covered up by billions of dollars in subsides and exploitation of low wage workers.

If problem solving is isolated from memorized data, that means that it would be more efficient to use a model that is dedicated to solving problems without needing to memorize facts. Training an LLM takes time and resource, especially if it is being trained on problems that are irrelevant to one’s work.

The isolation of problem solving implies that an LLM has the capability to categorize data and place the data in different parts of a model. As a result, problem solving capabilities for different subjects would also be split up. If a model is trying to reason about astronomy, it wouldn’t make sense for the model to be influenced by facts about history. which means that LLMs would use different model sections to reason about the two subjects.

If one wants to use an LLM to look up facts, it would be more efficient to use one that is just trained on memorized data without any reasoning abilities. Also, tools such as Wolfram Alpha have existed for years to provide information about memorized facts, and they don’t make up information like LLMs sometimes do.

To put things into perspective, I have been regularly benefiting from AI when riding the Red Line of the DC Metro. It now uses automated train control(ATC) to drive trains, which has made train rides smoother. This is an example of task-specific AI, that works 100% of the time, and the ATC developers were able to save time by ignoring irrelevant data.

Sources

https://arstechnica.com/ai/2025/11/study-finds-ai-models-store-memories-and-logic-in-different-neural-regions

https://arxiv.org/abs/2510.24256

Fixing a bug with displaying the next recurrence of a weekly event with the help of automated testing.

On the morning of Sunday, November 9th, 2025, I deployed an update to dmvboardgames.com to show information about groups a user joined and dates of recurring events that a user had an RSVP to attend.

I navigated to my user page shortly afterwards in the morning and saw that it was showing Sunday, November 16th as the next event date for the two events I host on Sundays. Since the current date was November 9th, the next event dates should’ve been displayed on November 9th.

I narrowed down the root cause to the following line of code on the backend. It made the next event date 7 days in the future if the user member page was accessed on the day of an event, regardless of whether the current time was before or after the event start time.

LocalDate nextEventDate = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.valueOf(eventDay.toUpperCase())));

The test also had the start date in the future. I added a pause if the test was being run at midnight and rewrote the test case with the following code.

LocalDate expectedDate = LocalDate.now();
LocalDate startDate = eventFromDb.getStartDate();
assertEquals(expectedDate, startDate);

Then I rewrote the next event date calculation.

LocalDate nextEventDate = LocalDate.now();
DayOfWeek eventDayValue = DayOfWeek.valueOf(eventDay.toUpperCase());
if(!eventDayValue.equals(nextEventDate.getDayOfWeek())){
nextEventDate = nextEventDate.with(TemporalAdjusters.next(eventDayValue));
}

Afterwards, the test passed.

I then added another test case for an event that was on a day that wasn’t the current one.

After fixing another test case that was incorrectly using the next day as a next event occurrence, all the tests passed.

I merged the changes to the main branch, then deployed to the release branch, and created a new release.

After the release build was complete, I started a production deployment.

After the deployment, my user page correctly showed the next occurrence of the two events I host on November 9th.

Thoughts about the cooperative card game The Gang!

On Wednesday night, I played a cooperative card game called The Gang for around 2 hours. The Gang is a cooperative adaptation of Texas Hold’em where players have to guess how good their hand it relative to other players.

A round of The Gang starts with players drawing 2 cards and then taking a chip to represent how strong they think their hand is. The chips have a number of stars ranging from 1 to the number of players in the game. Then there are 3 more rounds where cards are revealed, players reasses the relative strength of their hand, and then take a chip. During the 4 round, the chips players take have to correctly represent the

There are 1326 possible poker hands. A binary search to find a value in data that has 1326 items would take up to 11 iterations.

There are 6! ways that players can take chips. As a result, the way players represent chips can represent up to 9 bits of information. Players have 3 rounds to take chips before they have to take chips correctly representing the strength of their hand. This represents 27 bits of information, which is not enough to communicate the exact contents of people’s hands, even when considering the fact that the game is played with a standard 52 card deck without duplicates.

I think the fact that players have to communicate the relative strength of their hand over multiple rounds instead of simplify taking chips to communicate the exact cards in their hand is a brilliant design.

View models and JavaScript UI libraries

I was looking at two JavaScript libraries, reactive, and ripple.js that use view models to dynamically render html. This made me think about the design of my framework, places.js, which uses a state based approach like the following example.

import {BaseDynamicComponent} from "@bponnaluri/places-js";
import {IS_LOGGED_IN_KEY, LOGIN_STORE} from "../../data/auth/LoginStore.ts";

export class OpenCreateGroupComponent extends BaseDynamicComponent {
  constructor() {
    super([{
      componentReducer:(data:any)=>{
        return {
          [IS_LOGGED_IN_KEY]: data?.loggedIn
        }
      },
      dataStore:LOGIN_STORE
    }]);
  }

  override getTemplateStyle():string{
    return `  
      <link rel="stylesheet" type="text/css" href="/styles/sharedHtmlAndComponentStyles.css"/>
      <style>
        a {
          color: white;
          text-decoration: none;
        }    
      </style>`
  }

  render(data: any){
    const url = data[IS_LOGGED_IN_KEY] ?
      `html/groups/create.html` :
      `/html/users/createAccount.html?message=Register_an_account_and_log_in_to_create_a_group`

    return `
        <a href ="${url}">Create group</a>`

  }
}

The data passed into the render method is a regular JavaScript object without type checking. For example, in the component below, if I tried to render the url using the templated string <a href ="${data.url}">Create group</a>`, the component would render a link with an undefined value.

Adding a view model would be one way of adding type checking to this code. I think it would also provide additional value for more complex components. However, I don’t think I should add view models to places.js.

The additional code needed to create view models would mean increased costs with maintenance complexity. Having less code means reduced maintenance complexity.

Adding view models to places.js would mean bugs in the framework that would cost time to fix. The extra complexity would also make it more difficult to make necessary updates to places.js in the future.

I want to make the source code of dmvboardgames.com and places.js approachable to anyone with a basic understanding of CSS, HTML, and Javascript. Adding view models would make the code less approachable.

  • The additional code needed to create view models would mean increased costs with maintenance complexity. Having less code means reduced maintenance complexity.
  • Adding view models to places.js would mean bugs in the framework that would cost time to fix. The extra complexity would also make it more difficult to make necessary updates to places.js in the future.
  • I want to make the source code of dmvboardgames.com and places.js approachable to anyone with a basic understanding of CSS, HTML, and Javascript. Adding view models would make the code less approachable.
  • If a developer decides to use view models with places.js, they can extend the BaseDynamicComponent class in places.js to implement view models. As a general rule, I think it is better for other developers to extend the framework to meet the needs of the product they are building. If I implement view models, it is likely that I won’t implement them in a way that precisely meets the needs of other developers. Also, for developers who decide not to use view models, they will be unnecessary complexity. If I add support for the optional use of view models, I’ll have to spend more time maintaining places.js , which will mean I have less time to focus on improving the quality of places.js.

It has been around a month since I last made an update to places.js. Over the past month, I have been actively using places.js to develop new UI components for dmvboardgames.com, and there haven’t been any bugs on the website related to places.js. Modifying a code that has been working reliably for an update that may not be useful is an unnecessary risk.

I’m also making significant changes to the backend to support users joining groups, and I’ll likely be making additional changes. These changes are likely to have bugs, and making changes to places.js will obscure the source of bugs. It is normal to have bugs in software, and it’s impossible to consistently write 100% bug free code on the first try. On the other hand, steps can be taken to make sure the source of a bug can be quickly identified, so that a change can be made to fix it.

If a developer decides to use view models with places.js, they can extend the BaseDynamicComponent class in places.js to implement view models. As a general rule, I think it is better for other developers to extend the framework to meet the needs of the product they are building. If I implement view models, it is likely that I won’t implement them in a way that precisely meets the needs of other developers. Also, for developers who decide not to use view models, they will be unnecessary complexity. If I add support for the optional use of view models, I’ll have to spend more time maintaining places.js , which will mean I have less time to focus on improving the quality of places.js.


To add type safety in the future, I think an alternative solution could be to look into somehow leveraging TypeScript as places.js already uses TypeScript.

In the immediate future, I think updating the constructor to use an updated reducer would be a good way of reducing complexity.

 componentReducer:(data:any)=>{
   let url = "html/groups/create.html"
   if(data?.loggedIn){
     url = "/html/users/createAccount.html?message=Register_an_account_and_log_in_to_create_a_group"
   }
   return {openUrl:url}
 },

Then, the render method could be rewritten as follows.
render(data: any){
 return ` <a href ="${data.openUrl}">Create group</a>`
 }

Using a test driven approach for group membership: Part 3

I’m working on adding functionality to dmvboardgames.com which will allow users to join groups and see information about the groups they are a part of. After implementing backend logic as described in my previous post, I worked on end-to-end testing with the UI.


When doing end-to-end testing, I realized some adjustments needed to be made to the backend to make sure data showed up correctly on the UI. I also deleted some unused code and did some refactoring to improve readability.

As usual, I ran all the backend integration tests to make sure everything was working as expected.



Below is a preliminary version of the user member page with some test data. I plan to update this page to include the day and time of each event and improve the styling.

When working on the UI, I’m relying on the following design choices to help reduce complexity.

  • Using a multi-page web site instead of a SPA(Single Page Application). Since dmvboardgames.com consists of multiple independent web pages, changes are isolated. I don’t have to be concerned about breaking unrelated functionality when I make a change.
  • Isolating component CSS styles with shadow DOM. This allows me to have styles scoped to those components only using standard CSS. Also, if I’m looking for a component style to update, I can find it in the component’s style definition. Here is an example from the member data component.
  • Using a framework for common tasks. On the UI, I use a framework places.js I developed for state-based UIs, managing asynchronous data fetching, and setting up components to use shadow DOM. Here is the source code for places.js: https://github.com/gatherspiel/places-js

Using a test driven approach for group membership: Part 2

I’m working on adding functionality to dmvboardgames.com which will allow users to join groups and see information about the groups they are a part of. After implementing test cases as described in my previous post, I worked on implementing the logic to make sure the tests pass.

Here are a couple of notes about design decisions I made with the code:

  • The service layer logic does permission checking, and calls a UserRepository instance to get the data. While writing unit tests on UserMemberService.java wouldn’t be complicated if I mocked the dependencies, I don’t think they are worth writing. The value of the unit tests isn’t high. On the other hand, the unit tests will have ongoing costs due to making automated tests harder to run, and adding additional code that needs to be maintained. They will also be duplicating test coverage from the integration tests.
  • In the service layer, for the joinGroup and leaveGroup functions, I pass in a user object to the UserRepository instance which runs a database query. Although only the user id is necessary, I decide to pass the whole user object to avoid parameter confusion with the group id which is also an integer.



The code changes are here: https://github.com/gatherspiel/backend/pull/243. The next step will be adding API endpoints, creating a UI, and then testing end-to-end

Using a test driven approach for group membership

I’m working on adding functionality to dmvboardgames.com which will allow users to join groups and see information about the groups they are a part of.

I decided to start on the backend by adding tests and skeleton logic. Here are the tests I added. https://github.com/gatherspiel/backend/pull/243/commits/2954bbfac36ea82b5074a2c606e4f71747060ce1

I then ran the tests, and they all unsurprisingly failed. Later, I will work on finishing the backend code to ensure the tests pass, and I’ll likely make some updates to the tests. I’m finding the approach of specifying the test cases first with placeholder logic to be very useful for figuring out how the code should be structured at a high level. The approach has also helped me avoid writing incorrect code because of a test case I wasn’t thinking about.