executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
abl-v12-learning"Reference guide for Progress OpenEdge ABL 12.7 (2023) — v10→v12 migration" INT64, ENUM, VAR shorthand, CATCH/THROW/FINALLY, JSON support, generic collections, safe navigation operator, server-side joins
Independent GitHub snapshot · Automated classification · Original documentation governs
name: abl-v12-learning compatibility: opencode completeness: 95 content-types:
Version: Progress OpenEdge 12.7 (May 2023)
Language: Advanced Business Language (ABL), also known as Progress 4GL
Scope: v10→v12 migration patterns and new v12 features
VAR INT x = 0. replaces verbose DEFINE VARIABLE+=, -=, *=, /= for cleaner codeobj?:Property?:Method() avoids null checksON ERROR with structured exception handlingREAD-JSON/WRITE-JSON instead of manual parsingList<T>, SortedSet<T>, HashMap<K,V> replace linked listsFOR EACH...JOIN reduces client round-tripsThis reference is designed for:
programming-abl-v10-learningprogramming-abl-v12-ui| Version | Release | Key Theme |
|---|---|---|
| 12.0 | 2019 | Server-side joins, Docker, 64-bit sequences default, STOP catchable by default |
| 12.1 | 2020 | High availability, performance, security enhancements |
| 12.2 | 2020 | PACKAGE-PRIVATE/PACKAGE-PROTECTED access, AES-GCM encryption, Docker DB |
| 12.3 | 2021 | VAR statement, compound operators (+=, -=), CHAR/INT full synonyms |
| 12.4 | 2022 | VAR with inline NEW, expression initialization, AGGREGATE statement |
| 12.5 | 2022 | List<T>, safe navigation ?:, override non-abstract properties, FINAL on properties |
| 12.6 | 2023 | SortedSet<T>, Java 17 support, TLS 1.3, LeakCheck diagnostics |
| 12.7 | May 2023 | HashMap<K,V>, HASH-CODE(), .NET 6, signed archive libraries |
The VAR shorthand, compound operators (+=, -=), and CHAR/INT keyword synonyms reduce boilerplate significantly.
/* v10 — Verbose */
DEFINE VARIABLE x AS INTEGER INITIAL 0.
DEFINE VARIABLE name AS CHARACTER INITIAL "".
ASSIGN x = x + 1.
ASSIGN name = name + " world".
/* v12 — Concise */
VAR INT x = 0.
VAR CHAR name = "".
x += 1.
name += " world".
Package-level access modifiers, property overriding, generics/collections, and the safe navigation operator (?:) bring ABL's OOP model closer to Java/C#.
Structured CATCH/THROW/FINALLY blocks (first available in 10.2B, fully developed by 12.x) replace the legacy ON ERROR + NO-ERROR model.
Complete JSON object model (JsonObject, JsonArray, ObjectModelParser) plus READ-JSON/WRITE-JSON on temp-tables and ProDataSets. JSON was completely absent in v10.
Built-in generic collections (List<T>, SortedSet<T>, HashMap<TKey,TValue>) eliminate the need for custom linked-list patterns.
64-bit sequences by default, multi-threaded database server, Docker support, OAuth2/SAML, TLS 1.3.
| Category | v10 | v12 | Status |
|---|---|---|---|
| INT64 data type | ❌ Not available | ✅ DEFINE VARIABLE x AS INT64 | Added in v10.1B |
| ENUM type | ❌ Not available | ✅ ENUM statement | New in v12+ |
| VAR shorthand | ❌ Must use DEFINE VARIABLE | ✅ VAR INT x = 0. | New in 12.3 |
| Compound operators | ❌ x = x + 1 | ✅ x += 1 | New in 12.3 |
| PACKAGE-PRIVATE access | ❌ Not available | ✅ PACKAGE-PRIVATE / PACKAGE-PROTECTED | New in 12.2 |
| Override non-abstract properties | ❌ Only abstract | ✅ Any non-final property | New in 12.5 |
Safe navigation ?: | ❌ Must use VALID-OBJECT checks | ✅ obj?:Property?:Method() | New in 12.5 |
| Generic collections | ❌ Not available | ✅ List<T>, SortedSet<T>, HashMap<K,V> | 12.5–12.7 |
| CATCH / THROW / FINALLY | ❌ Not in v10 (added 10.2B) | ✅ Full structured exception handling | Fully mature in v12 |
| BLOCK-LEVEL ON ERROR UNDO, THROW | ❌ Not available | ✅ Single directive covers all blocks | New post-v10 |
| JSON support | ❌ None | ✅ JsonObject, JsonArray, READ-JSON, WRITE-JSON | New in 11.x |
| SAX callbacks via OO class | ❌ Procedure-only | ✅ EVENT-HANDLER-OBJECT | Improved in v12 |
| DYNAMIC-INVOKE() | ⚠️ Limited | ✅ Full class-method dynamic invocation | Improved in v12 |
| SERIALIZE-ROW() for JSON | ❌ XML only | ✅ JSON + XML targets | New in v12 |
| STOP as catchable object | ⚠️ Required -catchStop flag | ✅ Default behavior — CATCH/FINALLY fire | Changed in 12.0 |
| 64-bit sequences | ⚠️ Optional | ✅ Default for new databases | Changed in 12.0 |
| Methods as async callbacks | ❌ Procedures only | ✅ Class methods as EVENT-HANDLER | New in 12.3 |
| Server-side joins | ❌ Not available | ✅ FOR+NO-LOCK, up to 10 tables | New in 12.0 |
HASH-CODE function | ❌ Not available | ✅ HASH-CODE(arg1, arg2) | New in 12.7 |
FINAL on properties | ❌ Not available | ✅ Prevent subclass override | New in 12.5 |
AGGREGATE statement | ❌ Not available | ✅ Server-side aggregation | New in 12.4 |
| Feature | Introduced | Category |
|---|---|---|
DATETIME data type | v10.0B | Data Types |
DATETIME-TZ data type | v10.0B | Data Types |
BLOB / CLOB field types | v10.0B | Data Types |
LONGCHAR data type | v10.0B | Data Types |
INT64 data type | v10.1B | Data Types |
CLASS / INTERFACE OOP | v10 | OOP |
CATCH / THROW / FINALLY | 10.2B | Error Handling |
JSON (JsonObject, JsonArray, READ-JSON, WRITE-JSON) | 11.x | JSON |
Progress.IO.MemoryOutputStream / MemoryInputStream | 12.0 | I/O |
STOP catchable by default (no -catchStop needed) | 12.0 | Error Handling |
| Indeterminate array resize without data loss | 12.0 | Language |
| 64-bit sequences enabled by default (new DBs) | 12.0 | Database |
| Server-side joins (FOR + NO-LOCK, up to 10 tables) | 12.0 | Database |
PACKAGE-PRIVATE access modifier | 12.2 | OOP |
PACKAGE-PROTECTED access modifier | 12.2 | OOP |
| Methods as socket / SAX callbacks | 12.2 | OOP |
require-return-values compiler option | 12.2 | Compiler |
| AES-GCM encryption in ABL | 12.2 | Security |
OpenEdge Application Archive (.oear) | 12.2 | CI/CD |
VAR statement (shorthand DEFINE VARIABLE) | 12.3 | Language |
CHAR and INT as full type keywords | 12.3 | Language |
Compound operators (+=, -=, *=, /=) | 12.3 | Language |
| Runtime resize of initialized indeterminate arrays | 12.3 | Language |
Methods as RUN ... ASYNC callbacks | 12.3 | OOP |
| Methods as SAX-READER callbacks | 12.3 | XML |
VAR with expressions and function calls | 12.4 | Language |
VAR with inline NEW | 12.4 | Language |
AGGREGATE statement (server-side aggregation) | 12.4 | Database |
| Override non-abstract properties | 12.5 | OOP |
FINAL on properties | 12.5 | OOP |
Safe navigation operator ?: | 12.5 | Language |
Progress.Collections.List<T> | 12.5 | Collections |
Generic type syntax <T> | 12.5 | Language |
Progress.Collections.SortedSet<T> | 12.6 | Collections |
OpenEdge.Core.Util.LeakCheck class | 12.6 | Diagnostics |
| Java 17 support | 12.6 | Platform |
Progress.Collections.HashMap<TKey, TValue> | 12.7 | Collections |
HASH-CODE function | 12.7 | Language |
Signed Archive Libraries (.apl) | 12.7 | Deployment |
.NET 6 support on Windows | 12.7 | Platform |
ABL Type Hierarchy View in Developer Studio | 12.7 | Tooling |
| Data Type | v10 | v12 | Notes |
|---|---|---|---|
CHARACTER | ✅ | ✅ | Max 32,767 bytes. Default "". Keyword alias CHAR (v12.3+). |
LONGCHAR | ✅ | ✅ | Up to 2 GB − 1 byte. Code-page aware. |
INTEGER | ✅ | ✅ | 32-bit signed: −2,147,483,648 to +2,147,483,647. Default 0. Keyword alias INT (v12.3+). |
INT64 | ❌ | ✅ | 64-bit signed: −9,223,372,036,854,775,808 to +9,223,372,036,854,775,807. Default 0. Added v10.1B. |
DECIMAL | ✅ | ✅ | Up to 50 significant digits, up to 10 decimal places. Default 0. |
LOGICAL | ✅ | ✅ | TRUE/FALSE/YES/NO/Unknown(?). Default NO. |
DATE | ✅ | ✅ | 1/1/32768 BC to 12/31/32767 AD. Default ?. |
DATETIME | ✅ | ✅ | DATE + milliseconds from midnight. Local time. Default ?. Added v10.0B. |
DATETIME-TZ | ✅ | ✅ | DATETIME + UTC offset. Stored in UTC. Default ?. Added v10.0B. |
RAW | ✅ | ✅ | Binary, ≤ 32K. |
MEMPTR | ✅ | ✅ | In-memory binary, up to 2 GB − 1 byte. |
ROWID | ✅ | ✅ | Unique database record identifier. Preferred over RECID. |
RECID | ✅ | ✅ | Legacy 32-bit record ID. Use ROWID instead. |
HANDLE | ✅ | ✅ | Pointer to an ABL object/widget. Interchangeable with WIDGET-HANDLE. |
WIDGET-HANDLE | ✅ | ✅ | Alias for HANDLE. |
COM-HANDLE | ✅ | ✅ | ActiveX/COM object reference. |
BLOB | ✅ | ✅ | Binary large object. DB/temp-table fields only. Up to 1 GB. Added v10.0B. |
CLOB | ✅ | ✅ | Character large object. DB/temp-table fields only. Up to 1 GB. Added v10.0B. |
CLASS | ✅ | ✅ | User-defined class object reference. Default ?. |
ENUM | ❌ | ✅ | Strongly typed named constants. Implicitly class-based. New in v12. |
DEFINE VARIABLE id AS INT64 INITIAL 0.
/* Large values that overflow INTEGER */
id = 9000000000000000000.
/* Use in database fields for large sequences, snowflake IDs, etc. */
TABLE-SCHEMA myTable:
DEFINE COLUMN myId AS INT64.
END TABLE-SCHEMA.
The ENUM type provides strongly typed named constants with implicit class backing.
/* Define an enum type */
ENUM OrderStatus:
PENDING.
PROCESSING.
SHIPPED.
DELIVERED.
CANCELLED.
END ENUM.
/* Use the enum */
VAR OrderStatus status = OrderStatus:PENDING.
/* Switch is type-safe */
SWITCH status:
CASE OrderStatus:PENDING:
DISPLAY "Waiting for processing".
CASE OrderStatus:SHIPPED:
DISPLAY "Out for delivery".
DEFAULT:
DISPLAY "Unknown status".
END SWITCH.
/* Iteration support */
FOREACH s IN OrderStatus:
DISPLAY s / ENUM-NAME(s).
END.
The VAR statement is a concise alternative to DEFINE VARIABLE.
Basic syntax:
VAR TYPE name [AS subtype] [= expression].
Examples:
/* Basic declarations */
VAR INT count = 0.
VAR CHAR name = "".
VAR DECIMAL price = 99.99.
VAR LOGICAL isActive = TRUE.
/* With expressions */
VAR INT sum = 1 + 2 + 3.
VAR DATE today = TODAY.
VAR INTEGER length = LENGTH("hello").
/* With function calls */
VAR CHAR json = GET-TEMP-JSON().
VAR INTEGER count = GET-RECORD-COUNT().
/* With inline NEW (12.4+) */
VAR Customer customer = NEW Customer().
VAR JsonObject payload = NEW JsonObject().
VAR Progress.Collections.List<STRING> names = NEW Progress.Collections.List<STRING>().
/* Arrays */
VAR INT scores[5].
VAR CHAR items[?].
/* Multiple declarations */
VAR INT x = 0, y = 0, z = 0.
MUST NOT:
VAR for database fields (only variables)VAR for temp-table definitions (use DEFINE TEMP-TABLE)VAR to shadow class properties or method parametersCHAR is now a full synonym for CHARACTER. INT is now a full synonym for INTEGER.
/* v10 */
DEFINE VARIABLE name AS CHARACTER.
DEFINE VARIABLE count AS INTEGER.
/* v12 */
VAR CHARACTER name.
VAR INT count.
/* Also valid (12.3+) */
VAR CHAR name.
VAR INT count.
Compound assignment operators reduce boilerplate for common operations.
VAR INT x = 0.
VAR CHAR text = "Hello".
VAR DECIMAL total = 100.00.
x += 1. /* Same as: x = x + 1 */
x -= 5. /* Same as: x = x - 5 */
x *= 2. /* Same as: x = x * 2 */
x /= 3. /* Same as: x = x / 3 */
text += " World". /* Same as: text = text + " World" */
total += 50.00. /* Same as: total = total + 50.00 */
v10 legacy pattern (anti-pattern):
/* v10 — Fragile, hard to trace */
ON ERROR UNDO, ERROR-HANDLER.
PROCESS-ORDER(orderNum IN):
/* ... logic ... */
IF NOT AVAILABLE order THEN
THROW ERROR "Order not found".
/* ... more logic ... */
END.
ERROR-HANDLER:
/* Single handler for everything */
DISPLAY ERROR-TEXT.
RETURN.
v12 structured pattern:
/* v12 — Structured, explicit, testable */
PROCESS-ORDER(orderNum IN):
ON ERROR UNDO, THROW.
TRY
VAR Order order = GET-ORDER(orderNum).
IF order = ? THEN
THROW NEW OrderNotFoundException("Order not found", orderNum).
UPDATE-ORDER(order).
COMMIT.
CATCH OrderNotFoundException AS e:
LOGGER:WARN("Order {1} not found", orderNum).
RETURN FALSE.
CATCH Progress.Lang.Exception AS e:
LOGGER:ERROR("Error processing order {1}: {2}", orderNum, e:Message).
RETURN FALSE.
FINALLY:
/* Always runs — cleanup here */
RELEASE-LOCK(orderNum).
END.
RETURN TRUE.
END.
Key principles:
ON ERROR UNDO, THROW at procedure/block levelTRY/CATCH/FINALLY for structured exception handlingFINALLY for cleanup code that must always run/* Define a custom exception hierarchy */
CLASS acme.OrderException INHERITS Progress.Lang.Exception:
CONSTRUCTOR PUBLIC OrderException(message AS STRING):
SUPER(message).
END CONSTRUCTOR.
END CLASS.
CLASS acme.OrderNotFoundException INHERITS acme.OrderException:
DEFINE PRIVATE PROPERTY _orderId AS INTEGER.
CONSTRUCTOR PUBLIC OrderNotFoundException(message AS STRING, orderId AS INTEGER):
SUPER(message).
_orderId = orderId.
END CONSTRUCTOR.
DEFINE PUBLIC PROPERTY OrderId AS INTEGER GET ONLY:
GET():
RETURN _orderId.
END GET.
END PROPERTY.
END CLASS.
/* Usage */
THROW NEW acme.OrderNotFoundException("Order not found", orderId).
/* Parse JSON string to JsonObject */
VAR CHAR jsonStr = ""{\"name\":\"John\",\"age\":30}\"".
VAR JsonObject obj = NEW JsonObject():LOAD-JSON(jsonStr).
/* Access properties */
DISPLAY obj:GetValue("name") AS CHARACTER.
DISPLAY obj:GetValue("age") AS INTEGER.
/* Read nested object */
VAR JsonObject address = obj:GetObject("address").
DISPLAY address:GetValue("city") AS CHARACTER.
/* Build JsonObject programmatically */
VAR JsonObject user = NEW JsonObject().
user:Put("name", "John").
user:Put("age", 30).
user:Put("active", TRUE).
/* Convert back to string */
VAR CHAR jsonStr = user:GET-JSON().
DISPLAY jsonStr.
/* Output: {"age":30,"name":"John","active":true} */
DEFINE TEMP-TABLE ttUser
FIELD id AS INTEGER
FIELD name AS CHARACTER
FIELD email AS CHARACTER
FIELD active AS LOGICAL.
/* Populate temp-table */
CREATE ttUser.
ASSIGN ttUser/id = 1
ttUser/name = "John"
ttUser/email = "john@example.com"
ttUser/active = TRUE.
/* Write temp-table to JSON */
VAR CHAR userJson = "".
WRITE-JSON(ttUser, userJson).
/* Read JSON back into temp-table */
DEFINE TEMP-TABLE ttUser2
FIELD id AS INTEGER
FIELD name AS CHARACTER
FIELD email AS CHARACTER
FIELD active AS LOGICAL.
READ-JSON(ttUser2, userJson).
DISPLAY ttUser2/name.
VAR JsonArray arr = NEW JsonArray().
arr:Add("Apple").
arr:Add("Banana").
arr:Add("Cherry").
/* Access by index */
DISPLAY arr:GetValue(0) AS CHARACTER. /* Apple */
/* Iterate */
FOREACH i FROM 1 TO arr:Count:
DISPLAY arr:GetValue(i - 1) AS CHARACTER.
END.
/* Convert to JSON string */
VAR CHAR jsonArr = arr:GET-JSON().
VAR Progress.Collections.List<STRING> names = NEW Progress.Collections.List<STRING>().
names:Add("Alice").
names:Add("Bob").
names:Add("Charlie").
/* Access by index (0-based) */
DISPLAY names:Get(0) AS CHARACTER. /* Alice */
/* Modify */
names:Set(1, "Robert").
/* Remove */
names:Remove("Charlie").
/* Count */
DISPLAY names:Count. /* 2 */
/* Contains check */
IF names:Contains("Alice") THEN
DISPLAY "Found Alice".
/* Clear */
names:Clear().
VAR Progress.Collections.SortedSet<STRING> uniqueNames = NEW Progress.Collections.SortedSet<STRING>().
uniqueNames:Add("Charlie").
uniqueNames:Add("Alice").
uniqueNames:Add("Bob").
/* Adding duplicate returns FALSE */
uniqueNames:Add("Alice"). /* Returns FALSE, not added */
/* Automatically sorted */
DISPLAY uniqueNames:First() AS CHARACTER. /* Alice */
DISPLAY uniqueNames:Last() AS CHARACTER. /* Charlie */
VAR Progress.Collections.HashMap<STRING, INTEGER> ages = NEW Progress.Collections.HashMap<STRING, INTEGER>().
ages:Put("Alice", 30).
ages:Put("Bob", 25).
ages:Put("Charlie", 35).
/* Get value */
VAR INT aliceAge = ages:Get("Alice").
DISPLAY aliceAge. /* 30 */
/* Check if key exists */
IF ages:ContainsKey("Bob") THEN
DISPLAY "Bob is in the map".
/* Remove entry */
ages:Remove("Charlie").
/* Count */
DISPLAY ages:Count. /* 2 */
/* Generate hash code for objects or values */
VAR INT hash1 = HASH-CODE("hello").
VAR INT hash2 = HASH-CODE("hello").
DISPLAY hash1 = hash2. /* TRUE - same input produces same hash */
/* Multi-argument hash */
VAR INT combined = HASH-CODE("firstName", "lastName", 12345).
/* Useful for custom Equals implementations and hash-based collections */
Package-level access modifiers allow sharing between classes in the same namespace.
/* File: acme/Base.cls */
CLASS acme.Base:
DEFINE PRIVATE PROPERTY _private AS STRING.
DEFINE PACKAGE-PRIVATE PROPERTY _packageLevel AS STRING.
DEFINE PACKAGE-PROTECTED PROPERTY _protected AS STRING.
DEFINE PUBLIC PROPERTY Public AS STRING.
END CLASS.
/* File: acme/Sibling.cls - Same package */
CLASS acme.Sibling:
METHOD PUBLIC VOID AccessBase(base AS acme.Base):
/* base:_private - ERROR: cannot access */
base:_packageLevel = "shared". /* OK: same package */
base:_protected = "inherited". /* OK: package-protected */
END METHOD.
END CLASS.
/* File: other/Outside.cls - Different package */
CLASS other.Outside:
METHOD PUBLIC VOID AccessBase(base AS acme.Base):
/* base:_private - ERROR: cannot access */
/* base:_packageLevel - ERROR: different package */
/* base:_protected - ERROR: different package */
base:Public = "public". /* OK: public */
END METHOD.
END CLASS.
?: (12.5+)Eliminates verbose null checks when accessing nested properties.
/* v10/v11 — Verbose null checking */
VAR Customer cust = GET-CUSTOMER(id).
IF cust <> ? THEN
IF cust:Address <> ? THEN
IF cust:Address:City <> ? THEN
DISPLAY cust:Address:City:Name.
END.
END.
END.
/* v12.5+ — Concise safe navigation */
VAR CHAR cityName = GET-CUSTOMER(id)?:Address?:City?:Name ?? "Unknown".
DISPLAY cityName.
Prior to 12.5, only abstract properties could be overridden. Now any non-final property can be overridden.
/* Base class with concrete property */
CLASS acme.Base:
DEFINE PUBLIC PROPERTY Value AS STRING GET. SET.
END CLASS.
/* Override with coded getter/setter (12.5+) */
CLASS acme.Derived INHERITS acme.Base:
DEFINE PRIVATE PROPERTY _value AS STRING.
DEFINE PUBLIC PROPERTY OVERRIDE Value AS STRING GET. SET.
END PROPERTY.
GET():
RETURN UPPER(_value).
END GET.
SET(newValue AS STRING):
_value = LOWER(newValue).
END SET.
END PROPERTY.
END CLASS.
Prevent property overriding in subclasses.
CLASS acme.FinalBase:
DEFINE FINAL PUBLIC PROPERTY Immutable AS STRING GET. SET.
END CLASS.
CLASS acme.FinalDerived INHERITS acme.FinalBase:
/* This would cause a compile error:
DEFINE PUBLIC PROPERTY OVERRIDE Immutable AS STRING...
*/
END CLASS.
/* v10 — Client-side join (slow) */
FOR EACH Customer NO-LOCK:
FOR EACH Order WHERE Order.CustId = Customer.Id NO-LOCK:
/* Process each combination */
END.
END.
/* v12 — Server-side join (fast) */
FOR EACH Customer NO-LOCK
JOIN Order WHERE Order.CustId = Customer.Id NO-LOCK:
/* Database performs the join */
END.
END.
/* Server-side join up to 10 tables */
FOR EACH Customer NO-LOCK
JOIN Order WHERE Order.CustId = Customer.Id NO-LOCK
JOIN OrderItem WHERE OrderItem.OrderId = Order.Id NO-LOCK
JOIN Product WHERE Product.Id = OrderItem.ProductId NO-LOCK:
DISPLAY Customer.Name,
Order.OrderDate,
OrderItem.Quantity,
Product.Name,
OrderItem.UnitPrice * OrderItem.Quantity AS Total.
END.
DEFINE VARIABLE x AS INTEGER with VAR INT xDEFINE VARIABLE x AS CHARACTER with VAR CHAR xx += 1 instead of x = x + 1CHAR/INT keywords instead of CHARACTER/INTEGER where appropriateON ERROR blocks with TRY/CATCH/FINALLYNO-ERROR flags; use structured error handlingFINALLY blocks for cleanup codeJsonObject:LOAD-JSON()READ-JSON/WRITE-JSON for temp-table serializationSERIALIZE-ROW(..., "XML") with SERIALIZE-ROW(..., "JSON")List<T>SortedSet<T>HashMap<TKey, TValue>HASH-CODE() for custom object hashingVALID-OBJECT() chains with safe navigation ?:PACKAGE-PRIVATE for package-level encapsulationFINAL on properties that should not be overriddenAGGREGATE statement for server-side aggregations| Skill | Purpose |
|---|---|
programming-abl-v10-learning | Original ABL v10 procedural programming reference |
programming-abl-v12-ui | UI/widget programming in ABL 12 |
Use this skill when:
Understand the Problem — Identify inputs, outputs, constraints, and edge cases. Checkpoint: Clearly define the problem statement and expected behavior.
Select Algorithm — Choose the most appropriate algorithm based on constraints (time/space complexity, data size, ordering requirements). Checkpoint: Justify the algorithm choice with complexity analysis.
Implement & Test — Write clean, readable code with type hints, docstrings, and edge case handling. Checkpoint: Verify correctness with multiple test cases including edge cases.
Analyze & Optimize — Review time/space complexity, identify optimization opportunities, and validate against benchmarks. Checkpoint: Confirm complexity bounds match analysis and all test cases pass.