DEV Community

Cover image for 7 Programming Riddles That Even Elite Coders Can't Solve
Nik Poltoratsky for UI Bakery

Posted on

7 Programming Riddles That Even Elite Coders Can't Solve

In 1996 Phil Karlton said:

There are only two hard things in Computer Science: cache invalidation and naming things.

This quote evolved in different ways and my favorite one is:

I’m not as experienced as the guys above however I have my personal list of annoying programming issues I’m facing frequently. This is this list - a collection of the complex topics in programming that have no solution yet backed by my stories (sure, original problems are included, looks like they’re eternal):

  1. Cache invalidation
  2. Scrolling
  3. When to upgrade?
  4. Naming things
  5. N+1
  6. Dates

Cache invalidation

In a nutshell, cache invalidation is a tough challenge. It’s been one of the most stubborn problems in programming for decades, and we're still hunting for the one solution to rule them all. Whenever this problem shows up, it eats away at our valuable time.

From my own programming journey, I've found that the major headache of cache invalidation isn't about deciding when it's time to invalidate the cache, balancing performance, and data accuracy. No, the real problem is Understanding that the problem comes from the cache.

The sequence usually unfolds like this:

  • The support team flags a concern - the customer changes things, and the system says it's okay, but the old information stays there.
  • You try to replicate it on your local machine or the dev or staging server – everything's flawless there.
  • Sure, you're diving in to understand more about the situation, asking questions such as:
    • Did the update really go through?
    • Is there a bug causing the system to claim success prematurely?
    • If the update was implemented, was the data refreshed properly?
  • After a long and thorough check, you find that everything seems fine, but frustratingly, the problem doesn't happen very often.
  • Later, after spending many hours trying to fix the issue, you discover a hidden if/else statement that was added by someone who left the company two years ago.
  • Stop. Pause. Think. F*ck it. Suddenly, you realize it’s a unique caching setup for a specific situation that you wouldn't expect unless you knew about it beforehand. But really, how could you have guessed?

That's just one-half of the problem. What if the caching system lurks outside your code? My favorite troublemaker here is Hibernate. For those familiar with Hibernate, you know exactly what I'm talking about. For those who aren't, let's just say you're in for a big surprise.

Even after looking through lots of articles and videos, every few months, I encounter errors because of Hibernate. When it comes to caching, Hibernate can be a ruthless monster, ready to consume your peace of mind unless you handle it with extreme caution.


Scrolling

elder scrolls

I have experience in both frontend and backend development, but with my roots in frontend, I've developed a firsthand understanding of the fascinating world of scrolls. Trust me, it's more twisted than one might expect.

Primarily, customization! These subtle elements in your browser are unique across different platforms - be it Mac, Linux, or Windows, and not to mention browsers such as Chrome, Safari, or Firefox. Even though their look differs everywhere, they resist you when you try to customize their look uniformly across platforms, which frustrates our design team.

The story takes another twist when we bring in our love for MacBooks and our preference for hiding scrolls in our browsers. Unintentionally, we blind ourselves to blatant scrolling issues that go off to see the world in production. Surprise, surprise, scrolls pop up in the most unexpected places!

To tackle this, we created a dedicated channel to post about any scrolling anomalies we come across:

default scroll in heading

default scroll in json editor

default scroll in actions

Meanwhile, our design ideal remains the humble "Small Neat Scroll”

correct slim scroll

As a countermeasure against unleashing erratic scrolls onto the production stage, we developed a simple yet effective technique: Set scroll visibility to 'always' in Mac settings. Voila! This proved to be an efficient stopgap solution to trim down such issues in production. Who said all solutions must be complex?

Also, credit to my teammate Alex for using Firefox when the rest of us were sailing on the Chrome ship. Thanks to this, many pesky CSS bugs met their end.

So, here's a pro tip: use different browsers. Sure, you can opt for a complex solution like setting up a browser stack for your CSS issues, but it's not the feasible choice for every team. However, testing your project in two or three major browsers that your client uses can drastically help you nip issues in the bud.

But hey, it's not just my team battling these scroll-based outlaws. As we discovered, the world of coding is a lot more scroll-stricken than we had thought.


When to upgrade?

To upgrade, or not to upgrade – that is indeed one of our biggest questions in programming. The question of 'When to upgrade' has been a long-standing challenge. Essentially, it comes down to two main factors:

  1. Developers are always keen on keeping step with the newest versions of libs and frameworks.
  2. Managers are motivated to roll out the latest product versions to clients.

Now, let's delve right in and weigh the pros and cons in this see-saw of upgrading:

Pros:

  • Performance Boost: Upgrades can enhance your software's performance.
  • New Features: Upgrades often come with exciting new features, upping your tech game.
  • Bug Fixes: If you've been struggling with certain bugs, upgrades might be your savior.

Cons:

  • Potential Bugs: Every new addition comes with the risk of bugs. Bug fixes can be time-consuming and lower your confidence in the new code.
  • Breaking Changes: Upgrades are often tagged along with breaking changes. For instance, with Node 16 support being dropped by Angular 17, the adaptation time considerably increases.

Applying upgrades can undoubtedly bring many benefits, but they can also be a Pandora's box of unforeseen problems. You might wonder – How much work would it take to transition from Node 16 to Node 18 in a project? A simple version change, or a time-consuming search for the correct versions of all packages?

Personally, I'm on the cautious side when it comes to upgrades. However, some teammates are keen on them, and together, we've found a balance to keep our software updated, or at least close to the latest versions with minimal disruptions.

This is the case now, but before…

Backtracking a few years, we had an intriguing encounter with a new client, keen on deploying our software (https://uibakery.io/) on-premise. We provided them with a deployment guide, and surprisingly, they came back with a security report pointing out hundreds of issues, with tens of them as critical vulnerabilities!

Interestingly, all the major problems in our images could have been fixed easily with a regular update – potentially saving us from losing the client! This incident made us realize the power of staying updated. Since then, we've made it a point to perform security scans before any rollout.

In conclusion, finding the right time to upgrade is all about striking a balance and understanding your project's specific needs.


Naming things

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
—- Martin Fowler

One of the simplest, yet crucial undertakings in any coding project, is to appropriately name things. Seeing is believing, so let's look at two bites of code:

The first one, as you'll agree is a riddle, demanding a thorough read to decrypt its purpose.

public class LN {
    public static void main(String[] args) {
        B b = new B();
        b.s(1, "Harry Potter");
        b.s(2, "Lord of the Rings");
        b.l();
    }
}

class B {
    HashMap<Integer, String> m;

    B() {
        m = new java.util.HashMap<>();
    }

    void s(int i, String t) {
        m.put(i, t);
        System.out.println("Book added: " + t);
    }

    void l() {
        System.out.println("Book List:");
        for (java.util.Map.Entry<Integer, String> e : m.entrySet()) {
            System.out.println("ID: " + e.getKey() + ", Title: " + e.getValue());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The second one, in contrast, is reasonably transparent - a mere skim leads to an understanding of its functioning.

public class LibraryManager {
    public static void main(String[] args) {
        Library library = new Library();
        library.addBook(1, "Harry Potter");
        library.addBook(2, "Lord of the Rings");
        library.displayBookList();
    }
}

class Library {
    private Map<Integer, String> bookMap;

    Library() {
        bookMap = new HashMap<>();
    }

    void addBook(int id, String title) {
        bookMap.put(id, title);
        System.out.println("Book added: " + title);
    }

    void displayBookList() {
        System.out.println("Book List:");
        for (Map.Entry<Integer, String> entry : bookMap.entrySet()) {
            System.out.println("ID: " + entry.getKey() + ", Title: " + entry.getValue());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Appropriate naming is not just important in code. In fact, it’s often even more vital during team discussions. In my team, during discussions, we from time to time find ourselves deeply involved in intense arguing. Eventually, we come to understand that we all agree, but we have been using different terms to express our ideas.

In my view, assigning clear, descriptive names, and spreading these terms consistently across the team is one of the most crucial tasks in any project.

Why is this still a thorn in the side for many? The answer's simple: there isn't an automated linter/test/validator that can certify your naming conventions. Each member of your team comes with a diverse background, varied experiences, and unique views regarding variable names. To create harmony among these different factors takes a lot of time. If anyone knows an easy solution for this, I'd really like to hear it!


N+1

A few months ago, my colleague Vlad, also the CEO, summoned me to review a newly raised pull request (yes, our CEO is coding and it's another pain in the ass 🙃). The said PR is meant to augment how our clients manage their users on our platform. We work with Spring Boot in the backend, and this project update included a large SQL query that was about 150 lines long. Here's how our discussion went:

Nik: Vlad, great work on your PR. However, using 150 lines of SQL might be too complex, especially for those who see it for the first time. If you're busy with CEO work and we need to handle it, it could be tough. How about we try doing it in Java instead?

Vlad: Sure, Nik. I foresaw some performance optimization, but I'll go with your judgment.

Nik: Do we have clients to whom this change could impact significantly?

Vlad: Not yet, but the scope could unfold.

Nik: Exactly. Let's rewrite it in Java to make it easier to read. If we face problems that need SQL, we can change our approach later.

Vlad: Sounds good.

Henceforth, the PR was rolled out to production without a hitch, until, of course, we began expanding it to our on-premises clients.

One of the on-premises clients had already invited a significant user base. However, the new user-management page, introduced by Vlad, took 3+ minutes to load 🥳. Of course, we had then run into the N+1 problem. I was like:

facepalm

The N+1 problem happens when you first make one query to get a group of main items (+1), and then you make additional queries to get related details for each of these main items (N). For N objects, you end up unleashing N+1 queries, akin to ordering several pizzas and then asking separately for each topping's ingredients!

Though the N+1 issue is relatively general, it became all too familiar to me with ORMs (Object-relational mappings), and REST-like APIs.

ORM

Imagine you have a fancy ORM (Object-Relational Mapping) tool. It fetches data from your database and simplifies your life until the N+1 problem steals the show. Ironically, ORMs mostly default to lazy loading – they grab only the primary object first, picking the associated objects only when required. It sounds apt until you're in an N+1 scenario. Suddenly, your database is overworking, and you're left pondering: "Did I just order a pizza, or did I start baking the dough myself?"

Let's take a look at Hibernate, an ORM tool I often use for examples. Imagine we have two things: Author and Book. These represent authors and their books in a library. In our example, each Book is connected to an Author using a special link called @OneToMany(mappedBy = "author"). This shows the relationship between the authors and their books.

@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "author")
    private List<Book> books = new ArrayList<>();
}

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @ManyToOne
    @JoinColumn(name = "author_id")
    private Author author;
}
Enter fullscreen mode Exit fullscreen mode

Suppose we aim at fetching a list of authors and their books through Hibernate. We might take this route:

List<Author> authors = entityManager.createQuery("SELECT a FROM Author a", Author.class).getResultList();

for (Author author : authors) {
    // This triggers the N+1 problem!
    List<Book> books = author.getBooks();

    // Do something with the books
}
Enter fullscreen mode Exit fullscreen mode

In this example, Hibernate starts loading authors eagerly. However, it doesn't immediately fetch their corresponding books. As we pivot through the getBooks() method for each author, Hibernate charges forth with separate SQL queries for each author to load their books. This encapsulates the quintessence of the N+1 problem.

But don't worry, fetch joins can adjust our query to quickly get both the related books and their authors together:

List<Author> authors = entityManager.createQuery("SELECT DISTINCT a FROM Author a LEFT JOIN FETCH a.books", Author.class).getResultList();
Enter fullscreen mode Exit fullscreen mode

With this, Hibernate collates both authors and their books in a single query, exiling the N+1 problem.

However, dealing with large datasets may not be easy, and debugging can be elusive if you don't know your way around. When facing performance issues around the database, my go-to step is to fully enable SQL logging. There, I spotted numerous queries, unraveling the N+1 problem at hand.

REST-like APIs

Now, onto the REST-like API territory. Visualize an API that returns a list of entities (like superheroes), each retaining a list of superpowers. Making an API request to get a list of heroes could trigger the N+1 problem. You obtain the list but would have to make an additional request for each hero's superpowers. It's like attending a superhero meet-and-greet, but each hero narrates their origin plot individually, and efficiency takes a hit.

Here's where GraphQL shines, enabling you to specify what data you require and its structure, avoiding excessive requests. Like ordering a combo meal instead of separate dishes, GraphQL encapsulates your request as a single comprehensive entity.

There are different ways to solve the problem, not just with GraphQL. Options like JSON:API work too. But mostly, GraphQL has become the most popular choice for this.

In summary, N+1 could potentially be the villain in the realm of programming, but gallant tools like GraphQL could come to the rescue and restore order to this digital pandemonium. Be wise, choose your heroes wisely.


Dates

how to use dates in Javascript

Yes, once in a while, we all get slapped by the confounding inconsistencies of 'Dates.'

Regrettably, I don't have an engaging story to share, but I do have this list of date-related puzzles that left me scratching my head last year:

  • Varying Formats: No, 'Dates' are not a fan of uniformity. They stroll into your system bearing different getups, sometimes even within the same database or API.
  • Time Zone Tango: The dance between different time zones for client and server can get quite confusing.
  • The World Clock: Imagine dealing with clients operating in various time zones!
  • The Hour Offset Conundrum: Not all time zones proudly wear the one-hour badge.
  • Server-Time Zone Mismatch: The servers wandering in various time zones while the database sticks to its own is chaotic.
  • Leap Year and Leap Second: Because 'Dates' like to jump around a bit.
  • The Calendar Conversion: Converting 'Dates' between different calendars (like Gregorian, Julian, Lunar) is a formidable task.
  • Time Zone Twisters: Handling time zones with irregular offsets or historical changes is nothing short of a rollercoaster ride.
  • Distributed Systems or Microservices: Managing time zones in these scenarios typically calls for some hair-pulling.
  • On-Premises Deployments: Valid time becomes a puzzle when dealing with clients' on-premises deployments.

Dealing with dates in programming is much more complicated than it seems. It's just one part of the bigger challenge. Working with dates can be very confusing. In my experience, problems with dates are more difficult than issues with caching or naming. Or at least more irritating.


Finally

Finally, I want to say that all those problems are generally solvable - you can solve them in each individual case - you can name one variable properly, you can deal with scrolls on individual pages, you can design how you work with dates in your system and you can make sure that this exact controller method doesn’t have N+1 problem.

However, solving them in general is too hard and I don't think is possible - you can’t somehow force your team to name all the variables, functions, and classes properly. You can’t make one simple setup and make sure that all the scrolls in the system are always identical. You can’t add a few tests and make sure that dates in your system work correctly, necessarily there will be someone who will skip your rules and accidentally break the system.

What can we do here? We’re trying to use the Pareto rule and get 80% of results using 20% of the time - doing small simple steps to prevent most of the issues. Doing code reviews, doing hacks as explained in the scrolls section (enabling scrolls everywhere), doing testing for crucial flows, etc. We know we can't catch everything, so there might still be problems when it's actually deployed to production.

PS

This is a list of programming issues that I find particularly irritating. What are the coding challenges that annoy you the most? Please share your thoughts with me!

PPS

Hit a comment if you like the off-by-1 problem explanation.

Top comments (5)

Collapse
 
lugovsky profile image
Uladzimir Luhouski

My personal list of the most annoying issues is led by the infamous java.lang.NullPointerException, commonly referred to as "undefined is not what you want it to be." The most challenging aspect is the uncertainty around the number of necessary null/undefined checks. Striking a balance between code readability and safety becomes a constant concern.

Collapse
 
artxe2 profile image
Yeom suyun

It's terrible that they made optional objects instead of optional chaining.

Collapse
 
kostyadanovsky profile image
Konstantin Danovsky

+1 to Dates, my brain physically starts to hurt when it's time to think about dates

Collapse
 
roma36 profile image
Roman Grinovski

100%

Collapse
 
roma36 profile image
Roman Grinovski • Edited

Nice article, Nick! I enjoyed reading it.

Ironically, just today, I fixed a bug related to the Date time zone. This issue arose because the frontend was sending the date in the browser's time zone, whereas the server was expecting UTC. Fortunately, there's a great library called date-fns-tz, which facilitates the easy conversion of any browser date to another time zone, like UTC, for instance.

P.S. Noticed a funny thing while leaving this comment – my phone's date was 24 Nov, but the dev.to platform is still showing it as 23 Nov! Seems like there's a date issue here too 🤣