
Запах кода 304: Исключение с нулевым указателем - как избежать нулевых ссылок, которые вызывают сбои во время выполнения
21 июня 2025 г.Я продолжаю писать о нулевых проблемах, но каждый день новость напоминает мне: NULL все еще жив и пинает.
TL; DR: Избегайте нулевых ссылок, которые вызывают сбои времени выполнения, используя надлежащую проверку и нулевые модели
Проблемы 😔
- Время выполнениясбои
- Большойинциденты и отключения
- Непредсказуемое поведение
- Тяжелая отладка
- Пользовательский разочарование
- Системная нестабильность
- Плохая надежность
ВGoogle Cloudслучай:
- Плохая обработка ошибок: код разбился вместо изящной обработки нулевых данных
- Нетфункции флагов: Новый код не был постепенно развернут с управлением безопасности
- Мгновенная глобальная репликация: плохие данные распространяются по всему миру, как вШоудстрик инцидент
- Без рандомизированного отборока: восстановление вызвало перегрузку инфраструктуры
- Неадекватное тестирование: сценарий сбоя никогда не проверялся во время развертывания
Решения 😃
- Избегатьноль
- Используйте NULL -проверки, если нули находятся за пределами вашего контроля (например, внешний API)
- Инициализировать значения по умолчанию
- Реализуйте охраны
- Использоватьнулевые объекты
- Не используйтеопционы
Рефакторинг ⚙
https://hackernoon.com/code-refactoring-tips-no-015-remove-null
Контекст 💬
12 июня 2025 годаОсновной отключениепроизошло на платформе Google Cloud.
Это повлияло на десятки сервисов Google Cloud и Google Workspace во всем мире примерно с 10:49 до 13:49 PDT (всего 3 часа), причем некоторые услуги занимают больше времени для полного восстановления.
Отключение было вызвано каскадным сбоем в системе управления API Google:
- Триггер:
29 мая 2025 года Google развернул новый код в «Управление услугами» (их система управления API), в котором были добавлены дополнительные проверки политики квот.
Этот код имел критический недостаток. Ему не хваталообработка ошибоки не был защищенфункции флаговПолем
- Неудача:
12 июня изменение политики, содержащее бланк/НУЛЕВОЙFields был направлен в глобальную базу данных, которую использует управление службой. Когда управление обслуживанием попыталось обработать эти пустые поля, он столкнулся с нулевым указателем в незащищенном пути кода, что приводит к тому, что двоичные файлы сбои в бесконечном цикле.
- Глобальное влияние:
Поскольку управление квотами является глобальным, эти поврежденные данные были воспроизведены по всему миру в течение нескольких секунд, что привело к сбою услуг в каждом регионе.
Исключения нулевого указателя случаются, когда вы пытаетесь получить доступ к методам или свойствам на объектах, которых нет.
Это происходит, когда переменные содержат нулевые ссылки вместо допустимых экземпляров объекта.
Проблема становится особенно опасной в производственных средах, где эти исключения могут разрушить ваше приложение и расстроить пользователей.
Такие языки, как Java, C#и JavaScript, особенно склонны к этой проблеме, хотя современные языковые функции и модели могут помочь вам полностью избежать этих сбоев.
Nulls были серьезной проблемой в индустрии программного обеспечения на протяжении десятилетий, однако инженеры -программисты продолжают игнорировать их, несмотря на предупреждения своего создателя.
https://hackernoon.com/null-the-billion-dollar-mistake-8t5z32d6?embedable=true
Пример кода 📖
Неправильно ❌
public class ServiceControlPolicy {
private SpannerDatabase spannerDB;
private QuotaManager quotaManager;
public void applyPolicyChange(PolicyChange change) {
// NULL POINTER: change can be null
Policy policy = spannerDB.getPolicy(change.getPolicyId());
// NULL POINTER: policy can be null from the database
String quotaField = policy.getQuotaField();
// NULL POINTER: quotaField can be null (blank field)
quotaManager.updateQuota(quotaField, change.getValue());
}
public void exerciseQuotaChecks(String region) {
// NULL POINTER: policies list can be null
List<Policy> policies = spannerDB.getPoliciesForRegion(region);
for (Policy policy : policies) {
// NULL POINTER: individual policy can be null
String quotaValue = policy.getQuotaField();
// NULL POINTER: quotaValue can be null before trim()
quotaManager.checkQuota(quotaValue.trim());
}
}
public boolean validatePolicyData(Policy policy) {
// NULL POINTER: policy parameter can be null
String quotaField = policy.getQuotaField();
// NULL POINTER: quotaField can be null before length()
return quotaField.length() > 0 &&
!quotaField.equals("null");
}
public void replicateGlobally(PolicyChange change) {
List<String> regions = getGlobalRegions();
for (String region : regions) {
// NULL POINTER: change.getPolicy() can return null
spannerDB.insertPolicy(region, change.getPolicy());
}
}
}
Справа 👉
public class ServiceControlPolicy {
private SpannerDatabase spannerDB;
private QuotaManager quotaManager;
public void applyPolicyChange(PolicyChange change) {
if (change == null) {
// Assuming it comes from an external API
// Beyond your control
change = new NullPolicyChange();
}
Policy policy = findPolicyOrNull(change.policyId());
String quotaField = policy.quotaField();
if (!quotaField.isEmpty()) {
quotaManager.updateQuota(quotaField, change.value());
}
}
public void exerciseQuotaChecks(String region) {
if (region == null || region.isEmpty()) {
// Assuming it comes from an external API
// Beyond your control
return;
}
List<Policy> policies = policiesOrEmpty(region);
for (Policy policy : policies) {
String quotaValue = policy.quotaField();
if (!quotaValue.isEmpty()) {
quotaManager.checkQuota(quotaValue.trim());
}
}
}
public boolean validatePolicyData(Policy policy) {
if (policy == null) {
// Assuming it comes from an external API
// Beyond your control
// From now on, you wrap it
policy = new NullPolicy();
}
String quotaField = policy.quotaField();
return quotaField.length() > 0;
}
public void replicateGlobally(PolicyChange change) {
if (change == null) {
// Assuming it comes from an external API
// Beyond your control
// From now on, you wrap it
change = new NullPolicyChange();
}
Policy policy = change.policy();
if (policy == null) {
// Assuming it comes from an external API
// Beyond your control
// From now on, you wrap it
policy = new NullPolicy();
}
List<String> regions = globalRegions();
for (String region : regions) {
spannerDB.insertPolicy(region, policy);
}
}
private Policy findPolicyOrNull(String policyId) {
Policy policy = spannerDB.policy(policyId);
return policy != null ? policy : new NullPolicy();
}
private List<Policy> policiesOrEmpty(String region) {
List<Policy> policies = spannerDB.policiesForRegion(region);
if (policies == null) {
// This is a good NullObject
return Collections.emptyList();
}
return policies.stream()
.map(p -> p != null ? p : new NullPolicy())
.collect(Collectors.toList());
}
}
class NullPolicy extends Policy {
@Override
public String quotaField() { return ""; }
@Override
public String policyId() { return "unknown-policy"; }
@Override
public Map<String, String> metadata() {
return Collections.emptyMap();
}
}
class NullPolicyChange extends PolicyChange {
@Override
public String policyId() { return ""; }
@Override
public String value() { return ""; }
@Override
public Policy policy() { return new NullPolicy(); }
}
Обнаружение 🔍
- [x]Полуавтоматический
Вы можете обнаружить потенциальные исключения из нулевого указателя, просмотрев код для прямых вызовов метода по объектам без нулевых проверок.
Линтеры могут исследовать возвратные значения по методам, которые могут вернутьНулевой, в поисках ненициализированных полей объектов и использования инструментов статического анализа, которые помечают потенциальные нулевые измерения.
Современные IDE часто выделяют эти проблемы с предупреждениями.
Теги 🏷
- Нулевой
Уровень 🔋
- [x]Средний
Почему биение важна 🗺
ВРеальный мир, объекты либо существуют, либо нет.
Когда вы правильно моделируете это в своей программе, вы создаете четкоеОдин-один перепискамежду реальностью и кодом.
Разрыв этой бикологии, позволяя нулевым ссылкам, создает фантомные объекты, которые существуют в вашем коде, но не в реальном мире, что приводит к сбоям, когда вы пытаетесь взаимодействовать с этими несуществующими объектами.
Если вы решите назвать свой номерной знак «Null», вы получитемного парковочных билетов
Поколение AI 🤖
Генераторы ИИ часто создают код с уязвимостями нулевых указателей, потому что они сосредоточены на сценариях счастливого пути.
Они часто генерируют вызовы методов без учета краевых случаев, когда объекты могут бытьНУЛЕВОЙ, особенно в сложных иерархиях объектов или при работе с внешними источниками данных.
Обнаружение ИИ 🧲
Инструменты искусственного интеллекта могут обнаружить и исправлять проблемы с нулевым указателем, когда вы предоставляете четкие инструкции о практике защитных программирования.
Попробуйте их! 🛠
Помните: помощники ИИ делают много ошибок
Предлагаемое подсказка: удалить все нулевые ссылки
Без надлежащих инструкций | С конкретными инструкциями |
---|---|
Чатгпт | Чатгпт |
Клод | Клод |
Недоумение | Недоумение |
Второй пилот | Второй пилот |
Близнецы | Близнецы |
DeepSeek | DeepSeek |
Meta ai | Meta ai |
Грок | Грок |
Qwen | Qwen |
Заключение 🏁
Исключения нулевого указателя представляют собой одну из наиболее распространенных ошибок времени выполнения в программировании.
Вы можете удалить большинство из этих сбоев, внедрив правильные нулевые проверки, используя шаблон проектирования NULL объекта и приняв практики защитного программирования.
Небольшие накладные расходы кода валидации значительно окупаются при стабильности приложения и пользовательском опыте.
Отношения 👩❤
https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-iii-t7h3zkv
https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-xliii
https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-xxxix
https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-xxvi
https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-xlii
https://hackernoon.com/code-smell-252-nullcustomer?embedable=true
https://hackernoon.com/code-smell-260-crowdstrike-null?embedable=true
Больше информации 📕
https://status.cloud.google.com/incidents/ow5i3ppk96rdumcb1ssw?embedable=true
https://www.wired.com/story/null-license-plate-ly-one-hacker-ticket-hell/?embedable=true
https://www.forbes.com/sites/zakdoffman/2019/08/14/hacker-gets-12000-in-parking-trickets-after-null-license-prate-trick-backfires/?embedable=true
Отказ от ответственности 📘
Кодовые запахи - моимнениеПолем
Я называю это своей ошибкой в миллиард долларов. Это было изобретение нулевой ссылки в 1965 году
Тони Хоар
https://hackernoon.com/400-thought-предвидение-software-engineering-quotes?embedable=true
Эта статья является частью серии Codesmell.
https://hackernoon.com/how-to-find-the-shooky-parts-of-your-code-part-i-xqz3evd?embedable=true
Оригинал