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 Off is the default: Under it an implicit narrowing conversion and a late-bound Object call compile, and the type is resolved when the statement runs (T2)
  • ✅ Option Strict On removes the late-bound path: The same code does not compile until the conversion or the reference is written explicitly
  • ✅ Interface and Implements separate a contract from its implementation: A class names the interface members it supplies
  • ✅ A nullable value type states an absence: Integer? is Nullable(Of Integer), and HasValue and Value test and read it
  • ❌ Nothing is compatible with every reference type: A reference may be Nothing, no declaration requires a test before use, the language offers no nullable reference annotation, and for a string "" = Nothing is True (T10)
  • ⚠️ A class hierarchy couples a derived class to its base: Inherits binds a class to the base’s virtual members, and an Interface with Implements is the alternative that avoids the chain

1.1.2 Construction

  • ✅ Sub New is a real constructor: A class declares it, Overloads gives several signatures, and MyBase.New runs the base constructor
  • ✅ ReadOnly and Const mark a fixed value: A ReadOnly field is set once, and a Const is fixed at compile time
  • ❌ Mutability is the default: A Dim variable and a property are writable, and only an explicit ReadOnly or Const changes that (C2)
  • ✅ A Structure is a value type with its own constructor: A copy of a structure is a copy of its values, and it is not collected
  • ✅ Using bounds the life of a resource: The block calls Dispose on every exit path
  • ⚠️ WithEvents binds the handler object to its source: A WithEvents field is tied to the events of one declared type, and Handles lists 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)
  • ✅ Using gives deterministic release for an IDisposable: The block calls Dispose on every exit, including the exceptional one
  • ✅ IDisposable is 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 Try is a convention (Er2)
  • ❌ A failure can be discarded by design: On Error Resume Next and an empty Catch both let a failure pass with no effect (Er3)
  • ✅ Try/Catch/Finally is a structured path: The Catch block holds the recovery code and Finally runs on every exit
  • ✅ The failure is selected by type: Catch ex As IOException chooses by exception type, and ex.InnerException carries the cause
  • ⚠️ The legacy On Error GoTo form remains: It binds the recovery to a line label and a sequence, beside the structured form
  • ⚠️ An Async failure arrives at the Await: An exception in an awaited task surfaces at the await, so the Try block must span it

1.1.5 Concurrency

  • ❌ Synchronisation is manual with no race detection: SyncLock guards a block, and the compiler analyses no program for a race (K3)
  • ✅ SyncLock is a language construct: It takes a lock on an expression and releases it at the end of the block
  • ✅ Async and Await state 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: SyncLock names 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 .dll or .exe runs 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 .vbproj is an SDK-style project built by dotnet 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 Off around 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 If and End Sub are 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: StringBuilder and 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: vbc is 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: Object and 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 .exe in 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, and End Sub read 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 Option statements change the language: Strict, Explicit, Infer, and Compare each 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, and Next, and a declaration repeats Dim and As (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 Option changes the parse: Option Strict On at the top of a file changes which lines compile
  • ⚠️ The language is case-insensitive: myValue and MyValue name 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 test runs 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 Off weakens 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 unsafe context 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.Message and ex.InnerException carry the detail to a handler
  • ✅ Option Strict On removes the late-bound call: The member is resolved by the compiler rather than by name at run time
  • ⚠️ On Error Resume Next can 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?

  • ⚠️ Runs on a virtual machine, above native code in the energy and time benchmarks [5]; a causal re-analysis attributes most of the gap to execution time [6].

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:

All published papers

References

[1]
Y. Loth, “On the nature of coupling: All coupling as shared resource access.” Zenodo, Aug. 2026. doi: 10.5281/zenodo.21925122.
[2]
Y. Loth, “Coupling as a consequence of cohesion: Decomposition, the coupling floor, and domain-imposed coupling.” Zenodo, Aug. 2026. doi: 10.5281/zenodo.21925201.
[3]
Y. Loth, “On the nature of cohesion: Cohesion as correctness relative to a partitioning rule.” Zenodo, Jul. 2026. doi: 10.5281/zenodo.21703193.
[4]
Microsoft, “Visual basic language specification.” Microsoft, 2024. Available: https://learn.microsoft.com/en-us/dotnet/visual-basic/
[5]
R. Pereira et al., “Energy efficiency across programming languages: How do energy, time, and memory relate?” in Proceedings of the 10th ACM SIGPLAN international conference on software language engineering (SLE), 2017, pp. 256–267. doi: 10.1145/3136014.3136031.
[6]
N. van Kempen, H.-J. Kwon, D. T. Nguyen, and E. D. Berger, “It’s not easy being green: On the energy efficiency of programming languages,” in Proceedings of the 40th IEEE/ACM international conference on automated software engineering (ASE), 2025. doi: 10.1109/ASE63991.2025.00131.
[7]
S. Joel, J. J. Wu, and F. H. Fard, “A survey on LLM-based code generation for low-resource and domain-specific programming languages,” ACM Transactions on Software Engineering and Methodology, 2025, doi: 10.1145/3770084.