The Ada programming language in 2026: how it works, and how to modernize legacy Ada safety-critical code
The Ada programming language has been flying aircraft, braking trains and steering spacecraft for four decades. The problem in 2026 is rarely the language. It is the Ada
| Review tracker | Review tracker | Review tracker |
|---|---|---|
| Reviewer | Status | Notes |
| Person | ||
| Person | ||
| Person | ||
| Person |
code base built with a toolchain whose vendor stopped shipping updates, running on a processor nobody fabricates, maintained by engineers close to retirement.
This guide is for engineering leads, architects and certification managers who own that code. The first part explains Ada properly: history, type system, packages, generics, tasking, contracts and SPARK. The second part covers the options for an aging Ada system (upgrade to Ada 2012 or Ada 2022, or translate to C++, Rust or C#) and what each costs in DO-178C evidence, with code. For the wider decision framework, start with our legacy software modernization guide.
Key takeaways
- Ada is a maintained ISO standard (ISO/IEC 8652:2023, Ada 2022). Upgrading within Ada is often the lowest-risk option.
- Most Ada code compiles under modern Ada after a short, documented list of fixes. The hard part is the compiler, target and runtime change.
- Translating Ada to C++ or Rust modifies approved software. Expect to re-run requirements-based tests and re-achieve structural coverage (MC/DC at Level A).
- Range types, fixed-point types, protected objects and tasks do not map one-to-one to C++. Each needs a deliberate, verified design decision.
- Ada and Rust address memory safety differently. SPARK goes further by proving absence of run-time errors.
What is the Ada programming language?
The Ada programming language is a statically and strongly typed, imperative and object-oriented language designed for the US Department of Defense and standardized as ISO/IEC 8652. It builds range checking, packages, generics, concurrency and (since Ada 2012) contracts into the language itself, which is why it is widely used in long-lived avionics, rail, defense and space software.
Ada's design goal was to catch errors at compile time where possible and at run time where not, and to keep large programs readable by people other than their authors. The Pascal-like verbosity is deliberate. It helps when a reviewer must sign off on every line.
A short history: from HOLWG to Ada 2022
In the mid-1970s the DoD was maintaining software in of languages and dialects. Per the history summarized on Wikipedia, the High Order Language Working Group (HOLWG) formed in 1975 to fix that, and its 1978 "Steelman" requirements demanded reliability, exception handling, run-time checking and built-in concurrency.
In May 1979 the "Green" proposal from Jean Ichbiah's team won the design competition. The language was named after Ada Lovelace, and its reference manual was approved as MIL-STD-1815 on December 10, 1980 (1815 is Lovelace's birth year). The DoD mandated Ada from 1991 and effectively dropped the mandate in 1997.
| Version | Standard | Headline additions |
|---|---|---|
| Ada 83 | ANSI/MIL-STD-1815A (1983), ISO 8652:1987 | Strong typing, packages, generics, tasks and rendezvous, exceptions, representation clauses |
| Ada 95 | ISO/IEC 8652:1995 | Tagged types (first ISO-standard object-oriented language), protected objects, child packages, requeue, delay until |
| Ada 2005 | Amendment, published 2007 | Interfaces, the Ravenscar profile in the standard, container library, overriding indicators |
| Ada 2012 | ISO/IEC 8652:2012 | Contracts (Pre, Post, predicates, type invariants), aspects, quantified and conditional expressions |
| Ada 2022 | ISO/IEC 8652:2023 | Parallel blocks and loops, delta aggregates, declare and reduction expressions, the Jorvik profile |
The ISO catalog lists the 2023 edition as current, already marked "to be revised," with an amendment in development. The Ada language is still moving.
Ada language features that matter for safety-critical code

You cannot plan an Ada modernization without knowing which features of the Ada programming language carry safety properties. When code moves to another language, those properties either move with it as explicit code or silently disappear.
Strong typing and range types
Ada types are distinct by name, not structure. Two integer types with identical ranges cannot be mixed without explicit conversion, and ranges are checked at run time unless the compiler proves the check unnecessary.
type Altitude_Ft is range -1_000 .. 60_000;
type Airspeed_Kt is range 0 .. 900;
type Heading_Deg is mod 360; -- modular: wraps at 360
subtype Cruise_Altitude is Altitude_Ft range 18_000 .. 45_000;
Alt : Altitude_Ft := 35_000;
Spd : Airspeed_Kt := 450;
Hdg : Heading_Deg := 350;
Hdg := Hdg + 20; -- 10, by definition of a modular type
Alt := Alt + 30_000; -- raises Constraint_Error at run time
Alt := Alt + Spd; -- compile-time error: different types
Ada also has fixed-point types (type Volts is delta 0.01 range 0.0 .. 32.0;), used heavily on processors without fast floating point. C++ and Rust have no direct equivalent.
Packages and separate compilation
A package has a specification (what other units see) and a body (the implementation). The compiler enforces the boundary.
package Fuel_Monitor is
type Pounds is range 0 .. 200_000;
procedure Record_Burn (Amount : Pounds);
function Remaining return Pounds;
end Fuel_Monitor;
package body Fuel_Monitor is
Current : Pounds := 150_000; -- state hidden inside the body
procedure Record_Burn (Amount : Pounds) is
begin
Current := Current - Amount; -- Constraint_Error if negative
end Record_Burn;
function Remaining return Pounds is (Current);
end Fuel_Monitor;
Packages are elaborated in an order the binder computes and checks at startup. That guarantee becomes a migration issue later.
Generics
Ada generics are explicitly instantiated, and their formal parameters declare exactly what the generic needs: a discrete type, a type with "<", a procedure with a given profile. The body is checked once against those formals. A generic package instance can hold its own state, so it is closer to a parameterized module than a C++ class template.
Tasking, rendezvous and protected objects
Ada 83 introduced tasks with the rendezvous model: a task declares entries, another task calls one like a procedure, and both are synchronized for the duration of the accept body.
task Logger is
entry Log (Msg : in String);
end Logger;
task body Logger is
begin
loop
select
accept Log (Msg : in String) do
Text_IO.Put_Line (Msg);
end Log;
or
terminate;
end select;
end loop;
end Logger;
Ada 95 added protected objects: passive, mutually exclusive data with entries guarded by barriers. They are the idiomatic way to share data between tasks.
protected Nav_Data is
procedure Publish (F : in Fix);
entry Wait_New (F : out Fix);
private
Current : Fix;
Fresh : Boolean := False;
end Nav_Data;
protected body Nav_Data is
procedure Publish (F : in Fix) is
begin
Current := F;
Fresh := True;
end Publish;
entry Wait_New (F : out Fix) when Fresh is
begin
F := Current;
Fresh := False;
end Wait_New;
end Nav_Data;
The Ravenscar profile
Full Ada tasking is hard to analyze for worst-case timing. The Ravenscar profile, standardized in Ada 2005, restricts it (pragma Profile (Ravenscar);) to something analyzable: a fixed set of library-level tasks, no select statements, at most one entry per protected object with a simple Boolean barrier, ceiling-priority locking and delay until for periodic work. Nav_Data above is Ravenscar-compliant. Ada 2022 adds Jorvik, a slightly relaxed variant. Ravenscar code maps to an RTOS far more easily than unrestricted Ada 83 tasking.
Contracts in Ada 2012
Ada 2012 moved assertions into the specification as aspects (preconditions, postconditions, subtype predicates and type invariants) visible to callers, tools and reviewers.
subtype Slot is Positive range 1 .. 64;
type Counters is array (Slot) of Natural;
procedure Increment (C : in out Counters; I : Slot)
with Pre => C (I) < Natural'Last,
Post => C (I) = C'Old (I) + 1;
subtype Even_Count is Natural
with Dynamic_Predicate => Even_Count mod 2 = 0;
With GNAT, these become run-time checks when assertions are enabled. With SPARK, they become proof obligations.
SPARK Ada and formal verification
SPARK is a subset of Ada designed for formal verification with the GNATprove tool. It removes features that defeat static reasoning (such as unrestricted aliasing and side effects in functions), uses Ada 2012 contracts as its specification language and adds an ownership model for pointers. AdaCore describes adoption as a ladder in its case study on reusable bounded stacks:
| Level | Goal |
|---|---|
| Stone | Code is within the SPARK subset |
| Bronze | Initialization and data flow are verified |
| Silver | Absence of run-time errors is proved (no overflow, no index out of range, no failed range check) |
| Gold | Key integrity properties are proved |
| Platinum | Full functional correctness against the specification |
Silver is the usual target. For Increment above, GNATprove proves the addition never overflows (the precondition rules it out) and that every caller satisfies the precondition. A caller that cannot be shown to comply fails proof at that call site, before any test runs.
For modernization, SPARK cuts both ways. It is a reason to stay in Ada, because no mainstream alternative offers comparable proof tooling. It is also a migration hazard: proofs do not survive translation. If your certification argument takes formal-methods credit under DO-333, leaving Ada means replacing that credit.
The GNAT compiler and where Ada runs today
GNAT is the GNU Ada compiler, part of GCC. It supports the language up to Ada 2022 and accepts older code through switches such as -gnat83. AdaCore sells and supports GNAT Pro, with embedded cross compilers and certifiable run-time libraries. For new projects, ada-lang.io is the community hub, with the Alire package manager, reference manual and tutorials.
Commonly cited Ada users include the Boeing 777 fly-by-wire software, the Eurofighter Typhoon, Saab Gripen and F-22, the French TVM cab signalling system and metro systems in Paris, London, Hong Kong and New York. The UK's iFACTS air traffic control tools were built in SPARK.
Ada was meant to consolidate the DoD's language zoo. Programs that predated it are why JOVIAL is still flying, and many platforms now carry a mix of JOVIAL, Ada and C.
Why Ada systems become modernization problems
The Ada programming language is maintained. The individual system often is not. Four pressures tend to arrive together.
Compiler obsolescence. Many Ada systems were built with commercial compilers from vendors that have since merged, exited or frozen their products, often hosted on end-of-life VAX, Sun or HP-UX machines. Every bug fix means resurrecting an old host image.
Target obsolescence. Ada code was often compiled for processors such as the MIL-STD-1750A or Motorola 680x0 with a vendor-specific runtime. When the board is replaced, the compiler changes anyway, which is the moment to ask whether the language should change too.
Implementation-defined behavior. Old code leans on what the standard leaves to the implementation: representation clauses, Unchecked_Conversion of records, address clauses for memory-mapped I/O, task priorities, entry-queue ordering and vendor pragmas. This is the real migration risk, because it changes silently.
Skills. Ada engineers exist, but the ones who know a specific 1980s vendor runtime are retiring. It is the same dynamic as the shrinking pool of legacy-language engineers generally, and it mirrors what happened to Fortran 77 code in engineering organizations.
Ada modernization options compared
The realistic paths for an Ada 83 system with certification requirements:
| Option | What changes | Certification impact | Best fit | Main risk |
|---|---|---|---|---|
| Retain and freeze | Nothing; preserve the old toolchain | Minimal | Systems near retirement | Host and hardware failure |
| Upgrade to Ada 2012/2022 on GNAT | Compiler, runtime, some source | New environment; re-verify new object code | Ada skills, long remaining life | Implementation-defined behavior |
| Translate to C++ | Language, compiler, runtime, often RTOS | Modification; new source-level evidence | Platform standardizing on C++ | Losing range tasking semantics |
| Translate to Rust | Language, compiler, runtime | As C++, with less avionics qualification precedent | New components, Rust-invested teams | Ownership conflicts with Ada aliasing |
| Translate to C# or Java | Language and platform | Usually non-airborne | Ground stations, mission planning | Real-time semantics lost |
Retaining is legitimate. If the system retires in five years and changes twice a year, a preserved toolchain beats any migration, a case we make in when not to modernize legacy systems.
For most long-lived airborne systems, upgrading within the Ada language is the first option to price. It keeps the type system and the engineers' mental model. Translation earns its place when the platform is consolidating on C++, the Ada team cannot be sustained, or tight integration with a large C++ code base dominates. Our Ada modernization service covers both paths, and the broader trade-offs are in our overview of modernization strategies.
Upgrading Ada to Ada 2012 or Ada 2022
The Ada 95 Rationale's incompatibilities annex lists what breaks between Ada 83 and Ada . The common cases:
- New reserved words. abstract, aliased, protected, requeue, tagged and until became reserved in Ada 95. Ada 2005 added interface, overriding and synchronized, Ada 2012 added some, and Ada 2022 added parallel. Identifiers with those names must be renamed.
- Character grew from 128 to 256 positions. Case statements over Character without an others branch become illegal, and arrays indexed by Character change size.
- Indefinite generic formals. A generic that was instantiated with String needs (<>) on its formal private type.
- Numeric_Error became a renaming of Constraint_Error. Handlers that name both in one handler list no longer compile, and handlers that name only one may catch more than before.
- Library package bodies that the language does not require become illegal unless you add pragma Elaborate_Body.
-- Ada 83: legal. Ada 95 and later: two compile errors.
Protected : Boolean := False; -- 'protected' is now reserved
exception
when Numeric_Error => Recover;
when Constraint_Error => Recover; -- same exception named twice
These are mechanical fixes. The expensive work is replacing vendor pragmas and runtime calls, rewriting address clauses for the new memory map, re-checking representation clauses for new endianness and alignment, and re-validating priorities and timing on the new processor. An upgrade within Ada is still a change of compiler and target, with the verification consequences below.
DO-178C implications of changing language, compiler or target
DO-178C, Software Considerations in Airborne Systems and Equipment Certification, was approved by RTCA in December 2011. The FAA recognizes it through Advisory Circular 20-115D (July 21, 2017), which also recognizes DO-330 for tool qualification and the supplements DO-331 (model-based development), DO-332 (object-oriented technology) and DO-333 (formal methods).
DO-178C is language-neutral. It defines objectives by software level, from Level A (catastrophic failure condition; 71 objectives, 30 with independence) down to Level E, as the DO-178C summary on Wikipedia tabulates. Changing language does not change your objectives. It changes how much existing evidence still applies.
What AC 20-115D says about legacy software
AC 20-115D, paragraph 9, asks applicants to assess legacy software's usage history, to conduct a software change impact analysis if modifications are required, and to make changes under section 12.1 of DO-178C, upgrading the development baseline where the old one is not acceptable. A full language translation is the largest modification you can make, and the impact analysis will say so.
Evidence you keep and evidence you regenerate
| Lifecycle data | Upgrade within Ada | Translate to C++ or Rust |
|---|---|---|
| System and high-level requirements | Reused | Reused, if behavior is preserved |
| Low-level requirements and design | Mostly reused | Reused only if the translation preserves structure; otherwise rewritten |
| Source code reviews | Delta review of changed units | Full review of new source |
| Requirements-based tests | Re-run on new object code | Re-run; test harness and stubs ported |
| Structural coverage | Re-measured on new build | Re-achieved on new source (MC/DC at Level A) |
| Object-to-source traceability (Level A) | Re-analyzed for the new compiler | Re-analyzed for the new language and compiler |
| Tool qualification | New compiler and coverage tools assessed | Plus the translator, if you take credit from it |
Structural coverage. Level A requires modified condition/decision coverage; the NASA/FAA practical tutorial on MC/DC is still the best explanation. Coverage is measured on the source you certify, so a translation that restructures decisions (turning and then into nested if statements, or expanding a generic into several functions) changes the MC/DC test set. Level A also requires verifying object code that does not trace directly to source. Ada's run-time checks generate such code, and so do C++ templates, exceptions and virtual dispatch, so that analysis redone for the new language.
Tool qualification. DO-330 provides the method. As a general rule, a tool needs qualification when it eliminates, reduces or automates a DO-178C process and its output is not verified. If translated source is reviewed, tested and covered like hand-written code, the translator usually needs no qualification. If you want credit from it (for example, skipping reviews because equivalence is "guaranteed"), you are in DO-330 territory. Settle this with your certification authority in the Plan for Software Aspects of Certification (PSAC) before work starts.
If the target is object-oriented C++, DO-332 adds objectives for inheritance, dynamic dispatch and substitutability. A structure-preserving translation that uses classes only as data containers avoids much of it.
A planning checklist for a certified Ada migration
- Classify every component by software level. Level D and Level A code deserve different strategies.
- Inventory implementation-defined behavior: representation and address clauses, Unchecked_Conversion, vendor pragmas, runtime calls, task priorities.
- Decide the target per component: Ada upgrade, C++, Rust or retain.
- Draft the change impact analysis and PSAC amendment; review them with the certification authority early.
- Choose a structure-preserving translation so low-level requirements and traceability survive.
- Port the test harness first and baseline requirements-based tests on the legacy build.
- Run the same tests on the new build and compare outputs, bit for bit where possible. Our guide to proving functional equivalence after a migration covers the mechanics.
- Re-achieve structural coverage, closing gaps with analysis rather than tests written to the code.
- Re-verify timing, stack usage and worst-case execution time on the new target.
Mapping Ada to C++: constructs, gotchas and code
If C++ is the target, generate modern C++ (C++17 or later, under a coding standard such as MISRA C++), not C++98. Our piece on moving legacy C++ to modern C++ covers that dialect.
| Ada construct | C++ mapping | Gotcha |
|---|---|---|
| Package spec and body | Namespace, header plus .cpp; body state in an anonymous namespace | Ada's checked elaboration order becomes C++'s unordered static initialization across translation units |
| Private types | Class with private members | Ada privacy is per package, not per type; friend relationships may be needed |
| Range types and subtypes | Checked wrapper template | C++ signed overflow is undefined behavior; Ada raises Constraint_Error |
| Modular types | Unsigned arithmetic with explicit % | mod 360 wraps at 360; uint16_t wraps at 65,536 |
| Fixed-point types | Scaled integers with explicit rounding | Ada rounding rules on conversion must be reproduced exactly |
| Records with representation clauses | Structs with explicit shifts and masks | C++ bit-field layout is implementation-defined |
| Generics | Templates (with C++20 concepts for formal parameter contracts) | Generic package state becomes per-instance class state |
| Exceptions | C++ exceptions, or error codes and a fault handler where exceptions are disabled | Unhandled exception in an Ada task ends that task; in std::thread it calls std::terminate |
| Tasks | RTOS tasks or std::thread | Ada tasks start at elaboration and masters wait for children |
| Rendezvous | Synchronous request/reply over a queue or semaphore pair | Selective accept and timed entry calls need explicit state machines |
| Protected objects | Mutex plus condition variable, or RTOS priority-ceiling mutex | Ravenscar assumes ceiling locking; std::mutex does not provide it |
Range types to checked types
A tag parameter keeps types distinct, and widening before the check makes overflow detected rather than undefined, as in Ada.
#include <cstdint>
[[noreturn]] void constraint_error(const char* where); // project fault handler
template <typename Tag, std::int64_t Lo, std::int64_t Hi>
class Ranged {
static_assert(Lo >= INT32_MIN && Hi <= INT32_MAX, "32 bits");
public:
constexpr explicit Ranged(std::int64_t v) : v_(static_cast<std::int32_t>(check(v))) {}
constexpr std::int32_t get() const { return v_; }
friend constexpr Ranged operator+(Ranged a, Ranged b) {
return Ranged(std::int64_t{a.v_} + b.v_); // widen, then check
}
private:
static constexpr std::int64_t check(std::int64_t v) {
if (v < Lo || v > Hi) constraint_error(__func__);
return v;
}
std::int32_t v_;
};
struct AltitudeTag {};
struct AirspeedTag {};
using Altitude_Ft = Ranged<AltitudeTag, -1000, 60000>;
using Airspeed_Kt = Ranged<AirspeedTag, 0, 900>;
// Altitude_Ft{35000} + Airspeed_Kt{450} does not compile, as in Ada.
Whether constraint_error throws, resets or enters a safe state is a system safety decision, not a translation detail.
Protected objects to C++
#include <condition_variable>
#include <mutex>
class NavData {
public:
void publish(const Fix& f) {
{ std::lock_guard<std::mutex> lk(m_); current_ = f; fresh_ = true; }
cv_.notify_one();
}
Fix wait_new() { // entry Wait_New ... when Fresh
std::unique_lock<std::mutex> lk(m_);
cv_.wait(lk, [this] { return fresh_; });
fresh_ = false;
return current_;
}
private:
std::mutex m_;
std::condition_variable cv_;
Fix current_{};
bool fresh_ = false;
};
This is equivalent on a desktop but not on a real-time target: Ravenscar's ceiling locking bounds priority inversion and std::mutex doesn't. On an RTOS, use a priority-ceiling mutex (for example POSIX PTHREAD_PRIO_PROTECT) and keep the one-entry, Boolean-barrier shape so timing analysis still holds. Ada code with nested select, timed entry calls and abort needs restructuring by an engineer. For large C++ estates, our C++ modernization service handles post-translation cleanup.
Ada vs Rust and the memory-safety debate

The government and industry push toward memory-safe languages has made "Ada vs Rust" a real question for program offices. A fair comparison:
The Ada programming language gives you bounds-checked arrays, no pointer arithmetic, accessibility rules that prevent most dangling references, and a culture of avoiding pointers through stack-allocated and unconstrained objects. Range types catch logic errors that neither C++ nor Rust catches by default. Ada is not absolutely memory-safe: Unchecked_Deallocation, Unchecked_Conversion and address overlays exist, and old embedded code uses them.
Rust gives you compile-time ownership and borrowing that rules out use-after-free and data races in safe code. Integer overflow panics in debug builds but wraps in release builds unless overflow checks are enabled, a semantic difference from Ada worth knowing before any translation.
SPARK narrows the gap, according to AdaCore's Quentin Ochem in a comparison of Ada, SPARK and Rust: its ownership model and borrow checker provide "the same level of guarantees as Rust," while Rust "doesn't natively provide" range constraints or a specification language for proof. AdaCore sells Ada, SPARK and Rust tooling, so read that as a vendor view, but the technical points are checkable.
Certification maturity favors Ada, with decades of DO-178 history. Ferrocene, a qualified Rust toolchain, lists ISO 26262 (ASIL D), IEC 61508 (SIL 3) and IEC 62304 (Class C), but at the time of writing not DO-178C. A Rust-based Level A program would be breaking ground with its certification authority.
For migration, Ada to Rust is a large semantic jump. Ada 83 code often shares mutable package state between tasks and aliases data through access values, patterns the borrow checker rejects. A faithful translation either wraps everything in unsafe or Mutex, defeating the point, or redesigns it, which is a rewrite. Rust is a strong choice for new components beside an Ada or C++ core, and the hardest mechanical target for million line Ada .
How deterministic translation handles Ada, and where it stops
Ada defeats pattern matching and large language models for one main reason: overloading. The same "+" or Put can resolve to a dozen subprograms depending on operand and expected result types. Correct translation needs full name and type resolution across every visible package and generic instantiation. A translator that guesses is wrong in ways that compile.
DMS, the program-transformation engine behind Modernize Software, works like a compiler front end: it parses Ada with a full grammar, resolves names and types, builds control-flow and data-flow graphsthen applies rewrite rules to . Modernize Software reports that its Ada work covers Ada 83 through later standards, including code mixing eras and vendor extensions, targets C++ for embedded code and Java or C# for ground software, and can run entirely on-premises. The approach is explained in how deterministic software transformation works.
Three properties matter for certified work:
- Determinism. Same input, same output, so a translation can be regenerated and the diff reviewed, unlike a generative model (see why GenAI alone is not enough for modernization).
- Structure preservation. Units, subprograms and control flow map one-to-one where possible, keeping low-level requirements and trace links usable.
- Flagging. Constructs that cannot be translated with certainty, such as Unchecked_Conversion of represented records, timed entry calls or abort, go to engineer review instead of being approximated.
The limits matter as much. No translator produces DO-178C evidence; tests, reviews, coverage and timing analysis still run on the result. RTOS tasking design is the program's decision. And translation preserves behavior, bugs included. Improving the design is a separate step.
Case example: what a 1.2M-line avionics migration teaches Ada teams
The closest public reference point is not Ada. Modernize Software reports that Semantic Designs' tools were used on the Northrop Grumman B-2 Spirit flight software, translating roughly 1.2 million lines of JOVIAL to C. JOVIAL and Ada 83 share the traits that matter: pre-1990s defense language, obsolete toolchain, implementation-specific behavior, airborne certification context. Our JOVIAL migration service describes that work.
Three lessons carry over to Ada:
- Translate the whole system with one rule set. Mixing hand and automated translation produces two code styles and two sets of review findings.
- Keep the old build alive until the new one passes the same tests. The legacy binary is the oracle.
- Separate translation from improvement. Reviewers can certify "same behavior, new language." They struggle with "same behavior, plus refactoring, plus a new RTOS and processor" in one step.
For Ada program, the equivalent (illustrative) plan would translate with full type resolution, map Ravenscar-style tasking to the target RTOS, convert range and fixed-point types to checked wrappers, and port the test harness first.
Frequently asked questions
1. Is the Ada programming language still used?
Yes. Ada is maintained as ISO/IEC 8652:2023 (Ada 2022), GNAT supports it, and it is in active use in avionics, rail signalling, defense, space and air traffic control. Much of the installed base is older Ada 83 and Ada 95 code, which is where modernization pressure comes from.
2. What is the Ada programming language used for?
Ada is used where failure is expensive or dangerous: flight controls, mission computers, train control, weapons systems, satellites and air traffic management. Its strong typing, range checks, built-in concurrency and contracts suit long-lived embedded systems certified under standards such as DO-178C or EN 50128.
3. What is the difference between Ada and SPARK?
SPARK is a subset of Ada designed for formal verification. It removes features that block static reasoning and uses Ada 2012 contracts as its specification language, so GNATprove can prove absence of run-time errors or functional correctness. Every SPARK program is an Ada program, built by the same compiler.
4. Is Ada memory safe?
Ada is far safer than C or C++ by default: arrays are bounds-checked, there is no pointer arithmetic, and accessibility rules prevent most dangling references. It is not absolutely memory-safe, because Unchecked_Deallocation, Unchecked_Conversion and address clauses exist. SPARK's ownership model closes those gaps within the SPARK subset.
5. Is Ada better than Rust?
It depends on the job. Ada offers range types, mature certification evidence and, through SPARK, formal proof. Rust offers compile-time ownership checking and a larger, faster-growing ecosystem. For DO-178C Level A today, Ada has far more precedent. For new non-avionics systems with a Rust-skilled team, Rust is reasonable.
6. Does DO-178C require Ada?
No. DO-178C is language-neutral and defines objectives, not languages; Ada, C and C++ all fly in certified software. The language changes how much effort objectives such as structural coverage and object-to-source traceability take. Changing language on approved software is a modification that needs a change impact analysis.
7. Can Ada code be converted to C++ automatically?
Much of it can, if the translator performs full Ada name and type resolution. Packages, records, subprograms, generics and most expressions translate mechanically. Range and fixed-point types need checked wrappers, while complex tasking, representation clauses and unchecked conversions need engineer review. The result is verified like any new source.
Start with an assessment of your Ada code base
The right path for an Ada depends on measurable facts: how much implementation-defined behavior it uses, how its tasking is structured, which components are Level A, and how much test evidence can run on a new build. Modernize Software offers a free codebase assessment covering complexity, automation potential and a rough timeline in about five business days.
Request an assessment, or read more about our approach to Ada migration and upgrade.

info@semanticdesigns.com
