Code Smell 208 - Null Island

Code Smell 208 - Null Island

Leader ●1 ●49 ●119
calendar_today ago β€’ schedule11 min read

You can avoid null if you try

TL;DR: Don't use null for real places

Problems πŸ˜”

  • Tight Coupling

  • Unexpected Results

Solutions πŸ˜ƒ

  1. Model unknown locations polymorphically

Context πŸ’¬

Null Island is a fictional place that sits at 0Β°N 0Β°E, at the intersection of the Prime Meridian and the Equator in the Atlantic Ocean.

Many GPS systems place data with missing or invalid coordinates at this exact point. That's where the name "Null Island" comes from.

There's no landmass at this location. It's open ocean.

This point has become a popular reference for geographic information systems (GIS) and mapping software, because it helps filter out errors in location data.

Data visualization specialists started using the term around 2008, after noticing that failed geocoding requests and invalid coordinate entries often defaulted to (0, 0).

Natural Earth, a public domain mapping dataset, deliberately includes a fictional one-square-meter island at that exact point to help catch geocoding errors. The trick works: researchers have found more than 300,000 Flickr photos and countless social media posts geotagged to Null Island, and during the COVID-19 pandemic, Johns Hopkins' tracking dashboard plotted confirmed cases there whenever the real location was missing.

Sample Code πŸ’»

Wrong 🚫

class Person(val name: String, 
             val latitude: Double,
             val longitude: Double)

fun main() {
    val people = listOf(
        Person("Alice", 40.7128, -74.0060), 
        // New York City
        Person("Bob", 51.5074, -0.1278), 
        // London
        Person("Charlie", 48.8566, 2.3522), 
        // Paris
        Person("Tony Hoare", 0.0, 0.0) 
        // Null Island
    )
    
    for (person in people) {
        if (person.latitude == 0.0 && person.longitude == 0.0) {
            println("${person.name} lives on Null Island!")
        } else {
            println("${person.name} lives at " +
                    "(${person.latitude}, ${person.longitude}).")
        }
    }
}

Right πŸ‘‰

abstract class Location {
    abstract fun calculateDistance(other: Location): Double
    abstract fun ifKnownOrElse(knownAction: (Location) -> Unit,
        unknownAction: () -> Unit)
}

class EarthLocation(val latitude: Double, val longitude: Double): 
  Location() {
    override fun calculateDistance(other: Location): Double {
        val earthRadius = 6371.0
        val latDistance = Math.toRadians(
            latitude - (other as EarthLocation).latitude)
        val lngDistance = Math.toRadians(
            longitude - other.longitude)
        val a = sin(latDistance / 2) * sin(latDistance / 2) +
          cos(Math.toRadians(latitude)) * 
          cos(Math.toRadians(other.latitude)) *
          sin(lngDistance / 2) * sin(lngDistance / 2)
        val c = 2 * atan2(sqrt(a), sqrt(1 - a))
        return earthRadius * c
}
    
    override fun ifKnownOrElse(knownAction: 
      (Location) -> Unit, unknownAction: () -> Unit) {
        knownAction(this)
    }
}

class UnknownLocation : Location() {
    override fun calculateDistance(other: Location): Double {
        throw IllegalArgumentException(
            "Can't calculate distance" +
            " from an unknown location.")
    }

    override fun ifKnownOrElse(knownAction:
        (Location) -> Unit, unknownAction: () -> Unit) {
            unknownAction()
    }
}

class Person(val name: String, val location: Location)

fun main() {
    val people = listOf(
        Person("Alice", EarthLocation(40.7128, -74.0060)), 
        // New York City
        Person("Bob", EarthLocation(51.5074, -0.1278)), 
        // London
        Person("Charlie", EarthLocation(48.8566, 2.3522)),
        // Paris
        Person("Tony", UnknownLocation()) 
        // Unknown location
    )
    val rio = EarthLocation(-22.9068, -43.1729)
    // Rio de Janeiro coordinates

    for (person in people) {
          person.location.ifKnownOrElse(
              { location -> println("${person.name} is " +
                  "${location.calculateDistance(rio)} kilometers " +
                  "from Rio.") },
              { println("${person.name} is at an unknown " +
                  "location.") }
          )
      }
}

Detection πŸ”

[X] Semi-Automatic

You can check for special numbers used as nulls

Tags 🏷️

  • Null

Level πŸ”‹

[X] Intermediate

Why the Bijection Is Important πŸ—ΊοΈ

Real coordinates map to real places on Earth. That is the bijection between your model and the MAPPER.

When you reuse (0, 0) to mean "unknown location," you collapse two different concepts into a single representation.

A real point in the Atlantic Ocean and the absence of data aren't the same thing, and your model shouldn't pretend they are.

Modeling the unknown location as its own type keeps the mapping honest.

Real coordinates always mean a real place, and missing data gets its own explicit representation instead of borrowing one that already means something else.

AI Generation πŸ€–

AI generators create this smell often.

When you ask for a location class, they default to primitive latitude and longitude doubles and reach for (0.0, 0.0) as a convenient placeholder for missing data, the same shortcut developers take under deadline pressure.

AI Detection 🧲

AI generators rarely catch this smell on their own.

Unless you explicitly ask for a type that represents unknown locations, they treat (0.0, 0.0) as a normal default value and won't suggest polymorphism unless you request it.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: Replace the (0.0, 0.0) sentinel value with a polymorphic Location type that separates known coordinates from an explicit unknown location

Without Proper Instructions With Specific Instructions
ChatGPT ChatGPT
Claude Claude
Perplexity Perplexity
Copilot Copilot
You You
Gemini Gemini
DeepSeek DeepSeek
Meta AI Meta AI
Grok Grok
Qwen Qwen

Conclusion 🏁

Don't use Null to represent real objects

Relations πŸ‘©β€οΈπŸ’‹πŸ‘¨

https://coderlegion.com/9248/code-smell-12-null

https://maximilianocontieri.com/code-smell-126-fake-null-object

https://maximilianocontieri.com/code-smell-160-invalid-id-9999

More Information πŸ“•

https://coderlegion.com/6462/null-the-billion-dollar-mistake

https://en.wikipedia.org/wiki/Null_Island

A research buoy once sat right at 0Β°N 0Β°E collecting climate data until 2021, and the exact antipode of Null Island, at 0Β°N 180Β°E, is nicknamed "Antinull Island."

https://www.youtube.com/v/daiCb6pT1qY

Disclaimer πŸ“˜

Code Smells are my opinion.


The billion dollar mistake of having null in the language. And since JavaScript has both null and undefined, it's the two billion dollar mistake.

Anders Hejlsberg

https://coderlegion.com/13786/software-engineering-great-quotes


This article is part of the CodeSmell Series.

https://coderlegion.com/10942/how-to-find-the-stinky-parts-of-your-code

πŸ”₯ Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Code Smell 12 - Null

Maxi Contieri - Jan 4

Code Smell 320 - Vanity Coverage

Maxi Contieri - Jun 23

Code Smell 319 - Hardcoded Stateless Properties

Maxi Contieri - Apr 9

Code Smell 17 - Global Functions

Maxi Contieri - Feb 28

Code Smell 16 - Ripple Effect

Maxi Contieri - Feb 19
chevron_left
6.9k Points β€’ 169 Badges
Buenos Aires, Argentina β€’ maximilianocontieri.com
71Posts
5Comments
5Connections
Learn something new every day
Software Engineer and author of Clean Code Cookbook (https://amzn.to/4... Show more

Related Jobs

View all jobs β†’

Commenters (This Week)

4 comments
2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!