credit expiration
algorithm development
credit management
software engineering
programming support

Need help with credit expiration algorithm

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

A credit expiration system sounds simple until partial usage, different issue dates, and audit requirements enter the picture. The safest approach is to model credits as dated lots, consume them deterministically, and run expiration as a repeatable calculation rather than an ad hoc balance adjustment.

Model Credits as Individual Lots

Instead of storing only a single account balance, store each credit grant as its own record with:

  • amount issued
  • amount remaining
  • issue date
  • expiration date
  • source or reason

That structure lets you answer the hard questions later: which credits were spent, which ones expired, and why the balance changed on a specific date.

Here is a small Python model:

python
1from dataclasses import dataclass
2from datetime import date, timedelta
3
4
5@dataclass
6class CreditLot:
7    amount: int
8    remaining: int
9    issued_on: date
10    expires_on: date
11    source: str
12
13
14def issue_credit(amount: int, issued_on: date, valid_days: int, source: str) -> CreditLot:
15    return CreditLot(
16        amount=amount,
17        remaining=amount,
18        issued_on=issued_on,
19        expires_on=issued_on + timedelta(days=valid_days),
20        source=source,
21    )

With that representation, the algorithm becomes much easier to reason about.

Spend the Oldest Eligible Credits First

Most systems use a first-expiring, first-used rule. That prevents newer credits from being consumed while older credits quietly expire in the background.

python
1from datetime import date
2
3
4def spend_credits(lots: list[CreditLot], amount: int, as_of: date) -> None:
5    eligible = sorted(
6        [lot for lot in lots if lot.remaining > 0 and lot.expires_on >= as_of],
7        key=lambda lot: (lot.expires_on, lot.issued_on),
8    )
9
10    to_spend = amount
11
12    for lot in eligible:
13        if to_spend == 0:
14            break
15
16        used = min(lot.remaining, to_spend)
17        lot.remaining -= used
18        to_spend -= used
19
20    if to_spend > 0:
21        raise ValueError("insufficient non-expired credits")

This rule is deterministic. If the same lots and the same transaction date go in, the same answer comes out every time. That is exactly what you want for support investigations and accounting reconciliation.

Run Expiration as a Separate Step

Expiration should be its own calculation, usually at a daily cutoff or during balance reads. Do not mix it implicitly into unrelated write operations.

python
1def expire_credits(lots: list[CreditLot], as_of: date) -> int:
2    expired_total = 0
3
4    for lot in lots:
5        if lot.remaining > 0 and lot.expires_on < as_of:
6            expired_total += lot.remaining
7            lot.remaining = 0
8
9    return expired_total

Now you can process an account in a clear order:

  1. expire old lots
  2. apply new grants
  3. apply spending events
  4. compute the visible balance

That ordering prevents hidden state changes and makes replay testing much easier.

Example End-to-End Flow

python
1from datetime import date
2
3lots = [
4    issue_credit(100, date(2025, 1, 1), 30, "promo"),
5    issue_credit(50, date(2025, 1, 15), 60, "refund"),
6]
7
8expired = expire_credits(lots, date(2025, 2, 10))
9spend_credits(lots, 40, date(2025, 2, 10))
10
11balance = sum(lot.remaining for lot in lots)
12
13print("Expired:", expired)
14print("Balance:", balance)
15print([(lot.source, lot.remaining) for lot in lots])

That example is simple, but it scales because every balance change is grounded in a specific lot and date.

Design Decisions You Should Make Early

Before writing production code, define the business rules precisely:

  • Does a credit expire at the start of a day or the end of a day?
  • Are time zones based on the user, the business, or UTC?
  • Can expired credits be restored?
  • Can some credit types expire while others do not?
  • Does spending use earliest-expiring first, oldest-issued first, or something else?

If these rules are vague, no algorithm will stay correct for long.

Common Pitfalls

The biggest mistake is storing only a single integer balance. That loses the information needed to expire credits correctly and explain historic changes.

Another common problem is using the current clock directly inside business logic. Expiration code should accept an explicit as_of date or timestamp so tests are deterministic.

Teams also forget concurrency. If credits can be spent from multiple requests at once, the underlying database transaction must prevent the same remaining credit from being consumed twice.

Finally, be precise about date boundaries. A credit that expires on 2025-02-10 means nothing until you define whether it is valid throughout that day or only until midnight.

Summary

  • Store credits as dated lots, not just a single balance.
  • Spend credits in a deterministic order, usually earliest-expiring first.
  • Run expiration as an explicit step with a clear as_of time.
  • Make policy rules around time zones and day boundaries explicit.
  • Design for auditability and concurrency from the start.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.