Обратная совместимость

Обратная совместимость — это способность нового кода работать с API предыдущих версий. Для timeago.js она означает, что старые вызовы format(date) продолжают работать после обновления библиотеки.


Что считается обратно совместимым

Стабильные части API timeago.js — format, render, cancel, register — не менялись кардинально между мажорными версиями 3.x и 4.x. Это означает:

// Этот код работал в 3.x и продолжает работать в 4.x
import { format, render, cancel, register } from 'timeago.js';

format(new Date(), 'ru');
render(document.querySelector('time'), 'ru');
cancel(document.querySelector('time'));
register('custom', (n, i) => ['давно', 'скоро']);

Версионирование в semver

timeago.js следует semver:

  • 4.0.04.0.1: patch — только багфиксы, без breaking changes.
  • 4.0.04.1.0: minor — новые возможности, без breaking changes.
  • 4.0.05.0.0: major — breaking changes допускаются.
{
  "dependencies": {
    "timeago.js": "^4.0.0"
  }
}

^4.0.0 — разрешает обновления до 4.x.x, блокирует 5.0.0.


Стратегия поддержания обратной совместимости в собственном коде

Если приложение предоставляет API поверх timeago.js (например, React-компонент или утилиты), следовать тем же принципам:

// v1 — первоначальный API
export function formatDate(date: Date | string): string {
  return format(date, 'ru');
}

// v2 — добавить параметр без нарушения совместимости
export function formatDate(date: Date | string, locale = 'ru'): string {
  return format(date, locale);
}

// v2 обратно совместим: существующие вызовы formatDate(date) продолжают работать

Deprecation без удаления

Если нужно отказаться от части API, сначала пометить как устаревшее:

/**
 * @deprecated Используйте formatDate(date, locale) вместо этого.
 * Будет удалено в следующей мажорной версии.
 */
export function legacyFormatDate(date: Date): string {
  console.warn('[timeago] legacyFormatDate устарела. Используйте formatDate.');
  return formatDate(date);
}

TypeScript-декоратор @deprecated заставит IDE показывать предупреждение при использовании.


Полифилл для старых браузеров

Если приложение поддерживает старые браузеры (IE11, Safari 10), необходимо учитывать:

  • Promise — нужен полифилл для динамических импортов локалей.
  • Array.from — для конвертации NodeList.
  • Set, Map — для кеширования и реестров.
// Перед подключением timeago.js
import 'core-js/stable';

import { format, render, register } from 'timeago.js';

Абстрагирование от конкретной версии

Правильная архитектура скрывает зависимость от конкретной библиотеки за интерфейсом:

// src/services/time.ts — единая точка входа
import { format, render, cancel, register } from 'timeago.js';

export type TimeService = {
  format: (date: Date | string | number, locale?: string) => string;
  render: (el: Element, locale?: string) => void;
  cancel: (el?: Element) => void;
};

export const timeService: TimeService = {
  format: (date, locale = 'ru') => format(date, locale),
  render: (el, locale = 'ru')   => render(el, locale),
  cancel: (el)                  => cancel(el),
};

При смене библиотеки достаточно изменить реализацию в одном файле.


Тестирование на обратную совместимость

// Тест, запускаемый с каждой новой версией зависимости
describe('timeago.js API contract', () => {
  it('format принимает Date', () => expect(typeof format(new Date(), 'ru')).toBe('string'));
  it('format принимает string', () => expect(typeof format('2020-01-01', 'ru')).toBe('string'));
  it('format принимает number', () => expect(typeof format(Date.now(), 'ru')).toBe('string'));
  it('render не бросает исключение', () => {
    const el = document.createElement('time');
    el.setAttribute('datetime', '2020-01-01');
    document.body.appendChild(el);
    expect(() => render(el)).not.toThrow();
    cancel(el);
    document.body.removeChild(el);
  });
  it('register принимает кастомную функцию', () => {
    expect(() => register('test_compat', () => ['давно', 'скоро'])).not.toThrow();
  });
});

Фиксация версии в production

В production следует фиксировать точную версию:

{
  "dependencies": {
    "timeago.js": "4.0.2"
  }
}

Это гарантирует, что автоматические обновления не сломают production. Обновления выполняются осознанно с прохождением тестов.