Visual Basic 16 (.NET)
This assessment applies the coupling account — coupling as access to a shared resource, and the coupling floor [1], [2] — and the knowledge-embodiment theorem [3]; the language is assessed against its official reference [4].
1 Assessment
This page assesses Visual Basic .NET in five parts: core coupling — what the language’s own constructs force; ecosystem coupling — what the surrounding platform, tooling, and community cost; engineering readiness — whether the artifact can be reproduced, verified, observed, worked in quickly, connected to other languages, and governed openly; the profile — the unscored facts a score cannot express; and the scoring detail — the mechanism marks and the arithmetic behind every score. A ✅ bullet marks a construct that helps. A ⚠️ or ❌ bullet marks a cost; the scoring model does not price every cost named here.
Core coupling 46/100 · Ecosystem coupling 69/100 · Engineering readiness 88/100
1.1 Core coupling
1.1.1 Type
- ❌
Option Strict Offis the default: Under it an implicit narrowing conversion and a late-boundObjectcall compile, and the type is resolved when the statement runs (T2) - ✅
Option Strict Onremoves the late-bound path: The same code does not compile until the conversion or the reference is written explicitly - ✅
InterfaceandImplementsseparate a contract from its implementation: A class names the interface members it supplies - ✅ A nullable value type states an absence:
Integer?isNullable(Of Integer), andHasValueandValuetest and read it - ❌
Nothingis compatible with every reference type: A reference may beNothing, no declaration requires a test before use, the language offers no nullable reference annotation, and for a string"" = NothingisTrue(T10) - ⚠️ A class hierarchy couples a derived class to its base:
Inheritsbinds a class to the base’s virtual members, and anInterfacewithImplementsis the alternative that avoids the chain
1.1.2 Construction
- ✅
Sub Newis a real constructor: A class declares it,Overloadsgives several signatures, andMyBase.Newruns the base constructor - ✅
ReadOnlyandConstmark a fixed value: AReadOnlyfield is set once, and aConstis fixed at compile time - ❌ Mutability is the default: A
Dimvariable and a property are writable, and only an explicitReadOnlyorConstchanges that (C2) - ✅ A
Structureis a value type with its own constructor: A copy of a structure is a copy of its values, and it is not collected - ✅
Usingbounds the life of a resource: The block callsDisposeon every exit path - ⚠️
WithEventsbinds the handler object to its source: AWithEventsfield is tied to the events of one declared type, andHandleslists the events it answers, so the subscription is written into the declaration
1.1.3 Memory
- ❌ The garbage collector decides when memory returns: A reference is reclaimed after it becomes unreachable, and a collection pause is possible (M2)
- ❌ No release runs at the end of a scope: A class has a finalizer at best, whose time is not defined, and there is no deterministic destructor (M3)
- ❌ No ownership model: A reference may alias, and nothing in the type states who releases the object (M4)
- ✅
Usinggives deterministic release for anIDisposable: The block callsDisposeon every exit, including the exceptional one - ✅
IDisposableis the stated protocol for a native resource: A type that holds a handle states its release method - ⚠️ A finalizer delays the collection of its object: The object survives one extra collection while the finalizer queue is drained
1.1.4 Error
- ❌ The exceptions are unchecked: No method signature states the exceptions it may raise, so a
Tryis a convention (Er2) - ❌ A failure can be discarded by design:
On Error Resume Nextand an emptyCatchboth let a failure pass with no effect (Er3) - ✅
Try/Catch/Finallyis a structured path: TheCatchblock holds the recovery code andFinallyruns on every exit - ✅ The failure is selected by type:
Catch ex As IOExceptionchooses by exception type, andex.InnerExceptioncarries the cause - ⚠️ The legacy
On Error GoToform remains: It binds the recovery to a line label and a sequence, beside the structured form - ⚠️ An
Asyncfailure arrives at theAwait: An exception in an awaited task surfaces at the await, so theTryblock must span it
1.1.5 Concurrency
- ❌ Synchronisation is manual with no race detection:
SyncLockguards a block, and the compiler analyses no program for a race (K3) - ✅
SyncLockis a language construct: It takes a lock on an expression and releases it at the end of the block - ✅
AsyncandAwaitstate the suspension point: An asynchronous call reads as a sequence, and the language marks where it yields - ✅ The task and thread types come from the runtime:
Task,Thread, and the thread pool are .NET types the language uses directly - ⚠️ The lock order is not stated:
SyncLocknames a lock and not an order, so a deadlock is a design concern outside the language - ⚠️ An event handler runs on the raising thread: An event raised on a worker thread runs the handler there, and the UI-thread rule is a framework convention
1.1.6 Platform
- ✅ The compiler emits a portable .NET assembly: The resulting
.dllor.exeruns wherever the .NET runtime runs - ✅ The runtime runs on Windows, Linux, and macOS: The same assembly runs on each, so the language is not bound to one operating system
- ✅ The class library is reachable: An assembly with a public surface can be consumed from Visual Basic
- ✅ The project system is the common one: A
.vbprojis an SDK-style project built bydotnet build - ⚠️ A few newer features have no Visual Basic form: Nullable reference annotations and static abstract interface members have no equivalent syntax, so a consumer of a library that requires them needs a wrapper
- ⚠️ Windows Forms and WPF target Windows only: A GUI project on either framework runs on Windows
1.1.7 Trade-offs (not coupling)
- Late binding is the pragmatic COM path:
Option Strict Offaround an Office object model avoids an interop reference, which is why the setting survives - The Visual Basic 6 heritage is deliberate:
On Error,My, and the forms designer are continuity choices for the existing developer base - Verbosity buys explicitness:
End IfandEnd Subare redundant, and the end of every block is visible - The design-time form designer is a tool: The layout is a tool feature rather than a language mechanism
- A port to C# is mechanical: The constructs map closely, and the effort is the trade for leaving the language line
- A frozen language is stable for existing code: No feature churn reaches a maintained program
1.2 Ecosystem coupling
1.2.1 Performance
- ✅ The compiler emits IL and the runtime compiles it: The hot path becomes machine code through the just-in-time compiler
- ✅ The collector is generational: A short-lived object is reclaimed cheaply
- ✅ The runtime library supplies the fast paths:
StringBuilderand the asynchronous I/O APIs are available to a Visual Basic program - ⚠️ The first call of a method is the slowest: Compilation happens on first use, so start-up includes a warm-up
- ⚠️ Boxing and string concatenation allocate: An
Object-typed value and a repeated&produce garbage on the heap - ⚠️ A collection pause is possible on a large heap: The collector’s pause is outside the program’s control (R2)
1.2.2 Ecosystem
- ✅ NuGet is the package manager: A dependency is declared in the project and restored by
dotnet restore - ✅ The IDE is Visual Studio: Editing, refactoring, debugging, and the designer are integrated, and the free edition supports Visual Basic
- ✅ The class library is large and documented: I/O, networking, collections, and UI are covered by the runtime
- ✅ The compiler ships with the platform:
vbcis maintained with the .NET SDK - ⚠️ The published example is usually C#: A library’s documentation and samples are C#, and a Visual Basic consumer translates them
- ⚠️ New libraries are rarely written in Visual Basic: The language’s share of new code is small
1.2.3 Evolution
- ❌ The language line is closed to new features: Microsoft has stated that Visual Basic will not be evolved for new workloads, and the language receives no new syntax (V3)
- ✅ The language remains supported for existing code: The compiler is maintained with the .NET SDK for bug fixes and platform compatibility
- ✅ The runtime advances under the language: An existing assembly runs on a current .NET, so the platform moves even though the language does not
- ✅ A migration path to C# exists and is close: The language improved substantially from Visual Basic 6, and the constructs and the object model map almost one to one onto C#
- ⚠️ A new framework feature can be C#-only: Nullable reference types and static abstract interface members arrived without a Visual Basic form
1.2.4 Domain
- ✅ Office and COM automation is a native domain:
Objectand late binding make an Office object model usable without an interop assembly - ✅ Windows Forms is the rapid-desktop domain: The designer and the event model fit a business form application
- ✅ Console tools and services are straightforward: A short program builds into an
.exein the .NET toolchain - ✅ The .NET library ecosystem is reachable: Any assembly with a public surface can be consumed
- ⚠️ Modern .NET UI frameworks are not offered for Visual Basic: The .NET MAUI and Blazor templates are C#, so a new Visual Basic GUI is a Windows Forms or WPF project
- ⚠️ It is not the choice for new development: The language remains less expressive than C# on the newer features, and the direction of the ecosystem is C#
1.2.5 Learning
- ✅ The syntax is readable and close to English:
If ... Then,For Each ... In, andEnd Subread as their meaning - ✅ The curve is gentle from BASIC or Visual Basic 6: The surface carries over, and the runtime supplies the rest
- ✅ The reference documentation is complete: Microsoft documents every statement, keyword, and option
- ✅ The IDE guides a new user: IntelliSense and the compiler’s messages explain a construct as it is written
- ⚠️ Four
Optionstatements change the language:Strict,Explicit,Infer, andCompareeach alter what compiles or what a comparison means - ⚠️ The large subject is the class library: The framework, not the syntax, is the bulk of the work
1.2.6 Syntax
- ❌ The syntax is verbose: Every block closes with its own keyword pair such as
End If,End Sub, andNext, and a declaration repeatsDimandAs(S1) - ✅ Keywords are words: The reading is close to English
- ✅ A line ends a statement: Continuation is explicit with
_ - ✅ Attributes are written in angle brackets:
<Serializable>and<Obsolete("...")>attach metadata to a declaration - ⚠️ A file-level
Optionchanges the parse:Option Strict Onat the top of a file changes which lines compile - ⚠️ The language is case-insensitive:
myValueandMyValuename the same variable, so a rename is not caught by the case
1.2.7 Testing
- ✅ A test framework is available from NuGet: MSTest, NUnit, and xUnit all run against the .NET test SDK
- ✅
dotnet testruns the suite: Discovery, execution, and the report come from the platform - ✅ A test is written in Visual Basic: Attributes such as
<TestMethod>mark a test method - ✅ The debugger is integrated: A failed test can be stepped through in Visual Studio
- ✅ An assertion or mocking library is a package: NUnit’s assertions and Moq are consumed from NuGet
- ⚠️ The default
Option Strict Offweakens the test itself: A test project with the default setting can pass a value of a wrong type and notice only at run time
1.2.8 Security
- ✅ The runtime is memory-safe: No use-after-free and no buffer overrun occurs in safe code, and the language has no pointer type
- ✅ No
unsafecontext exists in Visual Basic: The unverifiable memory operations of the runtime are out of reach - ✅ The exception path keeps the failure in a value:
ex.Messageandex.InnerExceptioncarry the detail to a handler - ✅
Option Strict Onremoves the late-bound call: The member is resolved by the compiler rather than by name at run time - ⚠️
On Error Resume Nextcan hide a failure that matters: A suppressed failure leaves the program in a state the next statement does not expect - ⚠️ A COM interop boundary is outside the runtime’s checks: A late-bound call crosses into unmanaged code that the type system does not verify
1.3 Engineering readiness
1.3.1 Reproducibility
- ❌ Dependencies resolve without a lock by default.
1.3.2 Supply chain
- ⚠️ The package registry is a flat name space.
1.3.3 Observability
- ✅ No charged mechanism in this dimension.
1.3.4 Feedback
- ✅ No charged mechanism in this dimension.
1.3.5 Interop
- ✅ No charged mechanism in this dimension.
1.3.6 Governance
- ✅ No charged mechanism in this dimension.
1.4 Profile
Unscored, and kept separate from every score on purpose. The profile records what a mechanism price cannot express: what a running program costs, how well a coding model and an agent work in the language, and whether a safety-critical path exists.
1.4.1 Energy and cost
What this axis asks: what does a running program cost in energy and time?
1.4.2 AI and agent readiness
What this axis asks: how well does a coding model and an agent work in this language?
- ⚠️ Moderate training data: models write it competently, with measurable language-specific weakness [7].
1.4.3 Safety certification
What this axis asks: is there a certified or standards-qualified path to a safety-critical system?
- ⚠️ No certified or standards-qualified path for safety-critical systems.
1.5 Scoring detail
1.5.1 Core coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Type | T1 |
dynamic no static check | — | 0 |
T2 |
weak static | ✅ | −8 | |
T3 |
dynamic/duck dispatch | — | 0 | |
T4 |
no sum types | — | 0 | |
T5 |
no generics | — | 0 | |
T6 |
no namespaces | — | 0 | |
T7 |
type erasure/reflection | — | 0 | |
T8 |
operator overloading | — | 0 | |
T9 |
inheritance required for polymorphism | — | 0 | |
T10 |
no null-safety | ✅ | −8 | |
T11 |
no module boundary | — | 0 | |
T12 |
no effect tracking | ✅ | −8 | |
T13 |
async/sync colouring | — | 0 | |
T14 |
unchecked foreign boundary | ✅ | −4 | |
| Type subtotal | −15 (−28) | |||
| Construction | C1 |
global mutable state by default | — | 0 |
C2 |
no immutability by default | ✅ | −4 | |
C3 |
constructors coupled to allocation | — | 0 | |
C4 |
no constructor/validation | — | 0 | |
| Construction subtotal | −4 | |||
| Memory | M1 |
manual deallocation | — | 0 |
M2 |
GC non-deterministic pauses | ✅ | −8 | |
M3 |
no RAII | ✅ | −4 | |
M4 |
no ownership model | ✅ | −8 | |
| Memory subtotal | −15 (−20) | |||
| Error | Er1 |
no structured path | — | 0 |
Er2 |
unchecked exceptions only | ✅ | −8 | |
Er3 |
errors ignored/non-exhaustive | ✅ | −4 | |
Er4 |
no composable result type | — | 0 | |
| Error subtotal | −12 | |||
| Concurrency | K1 |
GIL/no parallel | — | 0 |
K2 |
shared mutable state default | — | 0 | |
K3 |
manual sync no race detection | ✅ | −8 | |
K4 |
no structured concurrency | — | 0 | |
K5 |
no primitives | — | 0 | |
| Concurrency subtotal | −8 | |||
| Platform | P1 |
cannot function outside one vendor/domain | — | 0 |
P2 |
single-vendor but other uses | — | 0 | |
P3 |
legacy/discontinued platform | — | 0 | |
| Platform subtotal | 0 |
Core Dimensions score: 46/100 — total cost = −54 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.2 Ecosystem coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Performance | R1 |
interpreted no AOT/JIT | — | 0 |
R2 |
GC pauses | ✅ | −8 | |
R3 |
JIT warmup | — | 0 | |
| Performance subtotal | −8 | |||
| Ecosystem | Ec1 |
no package manager | — | 0 |
Ec2 |
fragmented tooling | — | 0 | |
Ec3 |
small/niche/discontinued | — | 0 | |
Ec4 |
proprietary | — | 0 | |
Ec5 |
platform-locked tooling | — | 0 | |
Ec6 |
monoculture | — | 0 | |
Ec7 |
legacy burden | — | 0 | |
| Ecosystem subtotal | 0 | |||
| Evolution | V1 |
breaking changes | — | 0 |
V2 |
frozen | — | 0 | |
V3 |
discontinued | ✅ | −15 | |
V4 |
glacial standards | — | 0 | |
| Evolution subtotal | −15 | |||
| Domain | D1 |
domain/vendor lock-in | — | 0 |
D2 |
poor domain fit | — | 0 | |
D3 |
framework forces architecture | — | 0 | |
| Domain subtotal | 0 | |||
| Learning | L1 |
steep | — | 0 |
L2 |
large surface/multi-paradigm | — | 0 | |
L3 |
sparse docs | — | 0 | |
| Learning subtotal | 0 | |||
| Syntax | S1 |
verbose | ✅ | −8 |
S2 |
inconsistent | — | 0 | |
S3 |
punctuation-heavy | — | 0 | |
| Syntax subtotal | −8 | |||
| Testing | G1 |
weak tooling | — | 0 |
G2 |
poor debugging | — | 0 | |
G3 |
runtime-only errors | — | 0 | |
| Testing subtotal | 0 | |||
| Security | Q1 |
memory-unsafe | — | 0 |
Q2 |
dynamic/weak typing risk | — | 0 | |
Q3 |
known vulnerability/injection | — | 0 | |
| Security subtotal | 0 |
Meta Dimensions score: 69/100 — total cost = −31 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.3 Engineering readiness Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Reproducibility | Y1 |
build not reproducible | — | 0 |
Y2 |
dependencies not pinned | ✅ | −8 | |
| Reproducibility subtotal | −8 | |||
| Supply chain | Y3 |
no signed provenance | — | 0 |
Y4 |
registry without namespacing | ✅ | −4 | |
| Supply chain subtotal | −4 | |||
| Observability | Y5 |
no stable tracing standard | — | 0 |
Y6 |
no structured logging | — | 0 | |
| Observability subtotal | 0 | |||
| Feedback | Y7 |
slow build | — | 0 |
Y8 |
no fast in-editor analysis | — | 0 | |
| Feedback subtotal | 0 | |||
| Interop | Y9 |
no foreign-function interface | — | 0 |
Y10 |
no portable target | — | 0 | |
| Interop subtotal | 0 | |||
| Governance | Y11 |
single-vendor governance | — | 0 |
Y12 |
closed specification | — | 0 | |
| Governance subtotal | 0 |
Readiness Dimensions score: 88/100 — total cost = −12 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
Verdict: Visual Basic .NET reaches a mid-range core score by inheriting the .NET runtime’s separation of interface from implementation, its collected memory, and its checked types, while keeping the older Visual Basic defaults beside them. The losses are those defaults: Option Strict Off, a Nothing that fits every reference type, unchecked exceptions, a failure a caller may discard, and manual synchronisation. Main strengths: interfaces, Option Strict On, Using for deterministic release, the whole .NET class library, and a mechanical path to C#. Main weaknesses: Option Strict Off defers a conversion to run time, Nothing is not a checked absence, the collector decides when memory returns, a race has no detector, and the language line is closed to new features.
The formal development. The definitions, theorems, and proofs behind this page are published in:
- On the Nature of Coupling: All Coupling as Shared Resource Access — Zenodo, 2026 — doi:10.5281/zenodo.21925122
- Coupling as a Consequence of Cohesion: Decomposition, the Coupling Floor, and Domain-Imposed Coupling — Zenodo, 2026 — doi:10.5281/zenodo.21925201
- On the Nature of Cohesion: Cohesion as Correctness Relative to a Partitioning Rule — Zenodo, 2026 — doi:10.5281/zenodo.21703193