Blog › ICP guides

Turbo Pascal developer on retainer: ShortString truncation, typed file maintenance, unit interface management, and Turbo Pascal on monthly retainer

October 2, 2026 · ~13 min read

A Turbo Pascal developer was maintaining a Turbo Pascal 7.0 inventory management application for a small manufacturer. The application generated printed labels for stocked parts by building a summary description from four fields: part number, supplier name, technical specification, and internal notes. All four fields were concatenated into a single string variable declared as summary: string — the Turbo Pascal bare string type, which is a ShortString with a maximum length of 255 bytes. For most parts, the combined fields totalled 120 to 180 bytes. For three high-specification industrial parts, the technical specification field alone was 180 bytes; combined with the other fields, the total reached 295 to 310 bytes.

Turbo Pascal’s ShortString concatenation operator silently truncated the result at 255 bytes. No error was raised. No flag was set. The application printed labels for all parts without any diagnostic. The three high-specification parts had labels with truncated descriptions: the notes field was partially or completely absent from the printed output. The problem had existed since the notes field was lengthened in a data structure change the previous year; it only manifested for the three parts whose combined fields exceeded 255 bytes. Wrong labels: 3 → 0 after adding a Length check before concatenation and splitting long descriptions across two label lines using a string[80] array.

Turbo Pascal overview: the fast compiler that changed DOS development (1983–1995)

Turbo Pascal was released by Borland International in 1983, written by Anders Hejlsberg (who later designed C# and TypeScript). The defining feature was speed: the compiler ran entirely in memory on an IBM PC with 64 KB of RAM, compiled a full Pascal program in seconds, and cost $49.95 — a fraction of the cost of competing Pascal compilers, which ran on minicomputers and cost hundreds of dollars. Turbo Pascal’s IDE was the first integrated development environment that ran on a personal computer: editor, compiler, linker, and debugger in a single program. The combination of price, speed, and IDE made Turbo Pascal the dominant Pascal implementation on MS-DOS for a decade.

Pascal was designed by Niklaus Wirth at ETH Zurich and published in 1970 as a teaching language that enforced structured programming: no GOTO, explicit data types, and block structure inherited from ALGOL 60. Wirth intended Pascal as a tool for teaching correct program construction; Borland made it a tool for commercial software development. The Turbo Pascal compiler generated efficient native code for the 8086 and 8088; applications built with it were used in accounting, inventory management, scientific research, and engineering. Borland extended the Pascal language over successive releases: Turbo Pascal 5.5 (1989) added object-oriented programming with the object keyword; Turbo Pascal 7.0 (1992) added protected-mode compilation via DPMI; Borland Pascal 7.0 added DOS and Windows targets. The Delphi IDE (1995) was the successor, moving from DOS to Windows with a redesigned OOP model.

Turbo Pascal retainer work today covers applications that were written in the 1980s and 1990s and are still running on MS-DOS systems, DOSBox emulators, or have been forward-ported to Free Pascal. The programs include inventory management systems, scientific data processing utilities, accounting packages, and embedded industrial control systems that ran on early PCs. The reason they persist: they work, the data formats they produce are depended upon by other systems, and replacing them requires understanding their domain logic. Free Pascal (FPC) provides a current open-source Pascal compiler that is largely compatible with Turbo Pascal and Delphi syntax; the Lazarus IDE provides a modern development environment for Free Pascal. Migration from Turbo Pascal to Free Pascal is a common retainer engagement.

Turbo Pascal retainer work covers: ShortString length management and truncation diagnosis; typed file binary record maintenance; unit dependency resolution; pointer and heap management in real-mode DOS; and compiler directive configuration for {$R+} range checking, {$Q+} overflow checking, and {$I+}/{$I-} I/O checking. Each of these categories produces correctness bugs that are invisible without understanding Turbo Pascal’s specific implementation choices — particularly the ShortString internal format and the silent truncation behavior on overflow.

ShortString, string length, and silent truncation in Turbo Pascal

The Turbo Pascal string type is a ShortString: a fixed-size array of bytes where the first byte (index 0) stores the current length as a single unsigned byte, and bytes 1 through 255 store the string content. The maximum string length is 255 characters — not because of any algorithmic choice, but because the length field is one byte and a single byte can represent values 0 through 255. Declaring var s: string allocates 256 bytes: 1 length byte plus 255 content bytes. Declaring var s: string[80] allocates 81 bytes and caps the maximum length at 80. The internal structure is accessible: Ord(s[0]) returns the current length; assigning to s[0] directly sets the length without validation.

String concatenation in Turbo Pascal is performed by the + operator and the Concat() function. Both silently truncate the result if it would exceed the target variable’s declared maximum length. There is no exception, no error code, and no diagnostic. The truncation happens at the assignment to the target variable: the concatenation builds the full result, measures it against the target’s capacity, and stores only the portion that fits. The stored length byte reflects the stored portion, not the full result. Code that subsequently calls Length(s) receives the truncated length and sees a valid string; it has no way to detect that truncation occurred unless it independently measures the expected length from the source fields.

The diagnostic approach: before any concatenation that might exceed 255 bytes, sum the Length() values of all operands. If the sum exceeds the target capacity, truncation will occur. The fix depends on the application: for a string that must fit in one variable, either increase the declared length (e.g., change string[255] to string[80] per field and build a second summary line) or migrate to AnsiString in Free Pascal. In modern Free Pascal, the default string type is AnsiString: heap-allocated, reference-counted, with no 255-byte limit. Migrating from Turbo Pascal ShortString to Free Pascal AnsiString removes the truncation risk entirely but requires testing all code that accesses the string’s internal structure directly via s[0] (the length byte), since AnsiString does not support this access pattern.

A secondary string bug: the string[N] declaration caps the string at N characters, not N bytes. For applications processing data with non-ASCII characters (extended ASCII code page characters in Turbo Pascal applications), a string declared as string[40] can hold exactly 40 bytes; if code assumes it holds 40 display characters but the display system uses 2-byte sequences for some extended characters, the display truncates at a different position. This is less common than simple length overflow but appears in applications that handle currency symbols, accented characters, or box-drawing characters from the DOS code page.

Typed files, unit interface management, and pointer maintenance

Turbo Pascal typed files (FILE OF RecordType) store binary records in a format determined by the record type declaration at compile time. The file contains no schema: the reading program must use the exact same record type declaration as the writing program to read the records correctly. When a record type is modified — a field is added, a field type changes, a string field length changes — files written with the old record format cannot be read correctly with the new format. Each record in the file is a fixed number of bytes determined by the old record size; the new record size is different; reading the file with the new type misaligns every record after the first.

The most common typed file maintenance bug: a developer adds a new field to a record type used in a typed file, recompiles the application, and reads an existing data file. The application reads wrong values for every field after the first in every record, because the byte offsets for all fields after the insertion point are now different. No error is raised; the application reads valid bytes from the file, just at the wrong offsets. Diagnosis requires comparing the old and new record sizes (using SizeOf(RecordType)), identifying the field whose size or position changed, and writing a migration procedure that reads records using the old field layout and writes them with the new one. The migration procedure is typically 20 to 40 lines; the diagnosis and testing take 4 to 8 hours.

Turbo Pascal units are compiled separately; each unit has an interface section (exported declarations) and an implementation section (code). When the interface section of a unit changes, every unit that uses it must be recompiled. Turbo Pascal’s compiler tracks interface checksums; it refuses to link a program if any unit was compiled against an older version of a dependency’s interface. The most common unit dependency bug: a developer modifies the interface of a unit (e.g., changes a constant value, adds a type, changes a procedure signature), recompiles that unit, but does not recompile the dependent units. Turbo Pascal may accept the resulting binary during development but produce wrong behavior at runtime because the dependent unit was compiled with the old type sizes or constant values. The fix is a full recompile of all units in the project; the diagnosis requires recognizing that a unit dependency mismatch is not always a linker error but can produce silent wrong behavior.

Typical Turbo Pascal retainer work and what it looks like in a work log

ShortString truncation is the largest category of Turbo Pascal retainer work that produces no error diagnostic. The concatenation succeeds — from the program’s perspective, it assigned a valid string to a variable that can hold 255 bytes, and it did so correctly. The wrong output only appears when the source fields are long enough in combination. Work log entry: “BuildSummaryLabel in PrintInventory.pas: summary: string declared as ShortString, max 255 bytes; four fields concatenated: PartNo (8 bytes) + Supplier (45 bytes) + Spec (180 bytes) + Notes (62 bytes) = 295 bytes; Turbo Pascal truncated at 255 before assignment; last 40 bytes of Notes field absent from printed label; wrong labels: 3 (parts where Spec field exceeded 140 bytes); fix: added if Length(PartNo)+Length(Supplier)+Length(Spec)+Length(Notes) > 200 then split to two lines using LabelLine: array[1..2] of string[100]; wrong labels before: 3; after: 0; 5h.”

Typed file record alignment is the second category. A field addition misaligns every subsequent field in every record of an existing data file. Work log entry: “PartRecord type in PartData.pas: CatalogCode: string[12] field added after PartNumber: string[8]; old SizeOf(PartRecord) = 84 bytes; new SizeOf(PartRecord) = 97 bytes; existing parts.dat written with 84-byte records; reading with 97-byte type misaligned Supplier field by 13 bytes; wrong supplier names in all 1,200 records; fix: wrote MigratePartsFile procedure reading with old 84-byte fixed-field layout; rewrote all records with new type; wrong records before: 1,200; after: 0; 7h.”

Unit interface cascade is the third category. A constant change in a shared unit invalidates dependent units that were not recompiled. Work log entry: “MaxDescLength constant in Constants.pas changed from 80 to 120; Constants.pas recompiled; four dependent units (PrintUtils.pas, DataStore.pas, ExportFilter.pas, ImportFilter.pas) not recompiled; buffer allocations in dependent units still sized to 80; content longer than 80 bytes written to 80-byte buffer at runtime; stack corruption in ExportFilter; fix: full project recompile with tpc /m make flag; runtime errors before: 1 per export operation; after: 0; 3h.”

Track Turbo Pascal developer retainer hours without the status emails

When a 5-hour session diagnoses a ShortString truncation bug where three inventory labels had truncated descriptions — because Turbo Pascal’s string type silently truncates at 255 bytes with no error, no flag, and no diagnostic — the work log needs to name the procedure, the string declaration and maximum length, the concatenation sequence that exceeded the limit, what was truncated, and the wrong-label count. HourTab gives your Turbo Pascal retainer client a public dashboard URL they can bookmark: hours used, hours remaining, and a work log that names the Turbo Pascal mechanism. No client login. No status emails. CSV in, URL out.

See HourTab pricing →

How HourTab tracks Turbo Pascal developer retainer hours

Turbo Pascal retainer work is invisible by the same mechanism that makes ShortString reliable for the 99% of records where the concatenation stays under 255 bytes: no error fires, the program runs normally, and the output is correct for those records. The wrong output only surfaces for the specific records where the combined field lengths exceed the string capacity. An inventory database with 1,200 parts may have only 3 where the specification field is long enough to cause truncation. The printed labels look complete — all fields appear — until a customer or auditor compares the label to the original specification document and notices the truncated text. The connection between a missing 40-byte suffix on three labels and a 255-byte ShortString limit requires understanding Turbo Pascal’s internal string format.

The work log needs to name the mechanism: which procedure, which string variable and its declared maximum, which concatenation sequence exceeded the limit, how many bytes were truncated, what content was lost, and what the fix was. A log entry that says “fixed label bug, 5h” is not auditable. A log entry that says “BuildSummaryLabel: summary: string (ShortString max 255 bytes); PartNo+Supplier+Spec+Notes = 295 bytes; Turbo Pascal truncated at 255; last 40 bytes of Notes absent; wrong labels: 3; fix: split to two lines via string[100] array; wrong labels before: 3; after: 0; 5h” is auditable.

HourTab gives Turbo Pascal developers a public retainer-hours URL they send to clients — manufacturers running inventory management systems built in 1990, medical offices using patient record applications from the late 1980s, and engineering firms maintaining scientific utilities originally written in Turbo Pascal 5.0. For Turbo Pascal retainers, each work log entry should name the Turbo Pascal mechanism: which string type, which truncation limit, which record type misalignment. Comparative context: Turbo Pascal retainer work has conceptual overlap with other environments where string handling requires explicit size management — Delphi (Turbo Pascal’s direct successor, where the Delphi 2009 string type change from ShortString to UnicodeString introduced similar migration bugs); QBasic (another DOS-era language with fixed-length string types in TYPE records); and ALGOL (whose block structure and scope rules are the direct ancestor of Pascal’s design). Turbo Pascal is uniquely positioned as the language for the specific DOS-era business and scientific applications that run on systems too deeply integrated with their data formats and downstream processes to justify a replacement project.

FAQ: Turbo Pascal developer retainers

What does a Turbo Pascal developer on retainer typically do?

A Turbo Pascal developer on monthly retainer covers ShortString length diagnosis (Turbo Pascal string type maximum 255 bytes; Length() vs SizeOf() confusion; silent truncation in string concatenation and assignment; migration from ShortString to AnsiString in Free Pascal); typed file maintenance (FILE OF RecordType binary file operations; record structure alignment after field additions or type changes; Seek() and Reset() for random access; FileSize() and Eof() usage); unit interface maintenance (interface section declarations vs implementation section bodies; uses clause dependency ordering; circular unit dependencies; recompiling dependent units after interface changes); pointer and heap management (New() and Dispose() for pointer allocation; GetMem/FreeMem for raw bytes; heap fragmentation in long-running DOS applications); and compiler directive management ({$R+} range checking; {$Q+} overflow checking; {$I+}/{$I-} I/O checking).

What Turbo Pascal work is most commonly underlogged?

ShortString silent truncation is the most underlogged: a string variable with max 255 bytes silently truncates concatenation results that exceed the limit; no error fires; wrong output only appears for records where combined field lengths exceed the capacity; diagnosis requires summing Length() values of all operands against the target capacity; fix is split to smaller strings or migration to AnsiString; 4 to 6 hours. Typed file record misalignment: a field addition changes SizeOf(RecordType); existing data files read with new type produce misaligned fields for every record; fix is a migration procedure; 5 to 8 hours. Unit interface cascade: a constant or type change in a shared unit that is not fully recompiled produces wrong buffer sizes or stale constants in dependent units; fix is full project recompile; 2 to 4 hours.

What are typical Turbo Pascal developer retainer rates?

Entry-level Turbo Pascal developers with 1 to 2 years covering ShortString handling, file I/O maintenance, and basic Pascal procedure debugging typically bill at $55 to $100 per hour. Mid-level Pascal programmers with 2 to 4 years covering typed file maintenance, unit interface management, pointer management, and Turbo Pascal to Free Pascal migration typically bill at $85 to $155 per hour. Senior Turbo Pascal developers with 4 or more years covering complex object-oriented Pascal, large DOS application architecture, and Free Pascal or Lazarus migration planning typically bill at $120 to $220 per hour. Monthly retainer ranges: $800 to $2,000 per month for advisory engagements (10 to 20 hours per month); $2,000 to $6,000 per month for active application maintenance and migration work.

What should a Turbo Pascal developer retainer agreement include?

A retainer agreement should specify: Pascal version scope (Turbo Pascal 1.0 through 7.0; Borland Pascal 7.0; Free Pascal; Object Pascal or Delphi — each has different string types, object models, and platform targets); platform scope (MS-DOS real-mode, MS-DOS protected mode via DPMI, Windows via Free Pascal or Delphi); string type scope (ShortString analysis — whether length checking and truncation diagnosis are in scope; migration from ShortString to AnsiString is typically a separate project); typed file scope (whether binary file format maintenance is in scope; record structure alignment; file format changes require migration logic); compiler directive scope (whether {$R+}, {$Q+}, and {$S+} are enabled; these affect which runtime errors surface and which are silent); and hour logging format (the procedure or unit, the string type and length, the truncation mechanism, the fix applied, and the wrong-output count before and after).

How should Turbo Pascal developer retainer hours be logged?

Log each Turbo Pascal retainer session with: the procedure or code path where the bug appeared (e.g., BuildSummaryLabel in PrintInventory.pas); the string variable declaration and maximum length (e.g., summary: string — ShortString max 255 bytes); the concatenation sequence that exceeded the limit (e.g., four fields concatenated to 295 bytes, truncated to 255); what was truncated (e.g., last 40 bytes of Notes field); the fix applied (e.g., added Length() check; split to two-line array); and the wrong-output count before and after. For typed file bugs: the record type, the field that changed, the seek position that was wrong, and the count of corrupt records. For unit interface bugs: the unit name, the declaration that changed, the dependent units requiring recompilation, and the wrong-output count.