programming

Object-Oriented Programming a Failed Abstraction

Object-oriented programming (OOP) is built on abstraction, encapsulating both data and behavior into “objects.” While this approach has dominated software development for years, it doesn’t always align with how programs are created and executed. Often, the abstractions OOP introduces feel mismatched with the dynamic nature of software design and operation.

A key problem lies in how OOP connects data and operations. Take the example of a “person” object in a system. Attempting to define everything a person can do or be involved in within a single object quickly becomes impractical. People interact with systems in countless ways, such as being members of different groups, triggering workflows, or being part of external processes. Trying to encapsulate all these interactions within one abstraction leads to unnecessary complexity and rigidity. Software is not inherently about “things” or “objects”—it’s about tasks, processes, and services.

Much of software development is process-oriented. Applications often center around actions that need to be performed, such as validating business logic, fetching data, or completing a workflow. Similarly, functional programming approaches emphasize operations acting on data rather than tightly coupling both into objects. These paradigms reflect an underlying truth: software is primarily about what happens, not static representations of entities.

Thinking about software as “something that happens” can lead to cleaner, more practical designs. Programs are dynamic systems where tasks unfold over time based on input, rules, and workflows. By focusing on what needs to be done rather than forcing abstractions into objects, developers can design systems that align more closely with how software actually operates.

This doesn’t mean abandoning object-oriented programming entirely. OOP can be useful when modeling concepts with clear boundaries and well-defined behaviors, but it’s important to recognize that abstractions based on static objects are not always the best fit. Often, process-oriented thinking offers a simpler and more scalable solution, especially when software revolves around actions rather than entities.

Developers don’t need to follow a rigid paradigm, but rethinking abstractions can lead to better decisions. Software is dynamic, and understanding it as tasks, processes, and workflows rather than a collection of objects can help developers create more practical and adaptable solutions.

The Importance of Formatting in Code for Readability and Quality Assurance

Writing code that is clear and easy to understand is a fundamental goal in software development. Readable code allows developers to quickly grasp its purpose, evaluate its quality, and make improvements. This becomes especially critical when working with generated code, where much of the effort goes into reading, understanding, and quality-assuring what is produced, rather than writing it from scratch.

Despite this, formatting features that aid comprehension are often absent in code. There is little support for techniques like using line breaks to separate logical sections or indentation to show hierarchy and flow. Code does not allow for visual tools like highlighting or underlining to emphasize important elements. It lacks paragraph-like divisions to separate ideas and often doesn’t include clear headings or structured sections that help navigate larger chunks of work.

These missing formatting features are easy to take for granted in other forms of writing, such as documents or articles. In those contexts, they play a significant role in organizing content and guiding the reader. Without similar support in code, developers are left to navigate dense, unstructured blocks of text, making the process of understanding much more tedious.

This problem is amplified with generated code. Developers working with generated code often spend the majority of their time trying to read and understand it. Poor formatting adds unnecessary complexity, slowing down tasks like debugging, quality assurance, and feature development. More structured formatting would make generated code easier to interpret, reducing the time and effort required to work with it effectively.

By applying principles of formatting to code—such as better use of spacing, structure, and visual cues—developers can make their work less about deciphering and more about solving problems. Thoughtful formatting isn’t just about aesthetics; it’s about creating an environment where developers can focus on what truly matters, whether they’re writing code or working with what has been generated.

Revolutionizing Software Development with Application Specification Language (ASL)

Application Specification Language (ASL) introduces a higher-level approach to software development, focusing on describing what an application does instead of how it is implemented. Unlike programming languages, ASL operates at a level above traditional coding, providing a formal abstraction of software that is technology-agnostic. It offers a structured way to represent applications, allowing developers to define functionality without being tied to specific frameworks or languages.

ASL can be generated from existing code or derived directly from human-readable requirements and specifications. This flexibility allows it to bridge the gap between conceptual design and implementation. By capturing the software’s high-level concepts, ASL becomes the foundation for creating new implementations in any programming framework or language. It enables both upgrades and migrations, making it an effective tool for transitioning legacy systems to modern platforms.

Once ASL code is generated—either manually or by using a language model—it serves as a formal specification of the software system. From this specification, actual code can be created within diverse technologies. This abstraction dramatically simplifies development processes, as the focus shifts from implementation details to broader system functionality.

The practical benefits of ASL are significant. Its technology-neutral nature ensures that applications can be adapted, migrated, or upgraded without requiring extensive rewrites. It enables productivity by automating code generation and minimizes errors that come with manual coding efforts. Additionally, it future-proofs applications by decoupling them from specific languages or frameworks, making them more adaptable to emerging technologies.

ASL is particularly useful for migrating legacy systems, prototyping new software, and developing applications for diverse ecosystems. It streamlines workflows by automating repetitive tasks and ensures a consistent structure throughout development cycles. While ASL has many advantages, human oversight is still critical to ensure accuracy, especially when generating specifications from complex systems or existing codebases.

By revolutionizing how developers approach software design, ASL simplifies the development process and ensures smoother transitions between frameworks and technologies. Its focus on high-level abstraction empowers developers to innovate and create adaptable, sustainable systems for the future of software engineering.

Design Alternatives for Using Path, HTTP Methods, and Actions

When designing an API, choosing how to structure endpoints and model the interaction between client and server is a critical design decision. The three alternatives outlined – data-driven, object-oriented, and action/process-driven – represent different approaches with distinct strengths and weaknesses. The choice of approach should be based on both technical and business needs, as well as user expectations and workflows.


1. Data-Driven Approach

Description

This approach focuses on data as the primary entity in the API. Clients perceive the API as a system for storing and retrieving data, without directly interacting with actions or processes. Business logic and processing happen invisibly on the backend, and clients only see the results through the data produced.

Characteristics

  • Clear separation between data and processes.
  • Clients interact only with resources (e.g., submissions) and their lifecycle.
  • Process statuses are represented as fields in the data.
  • Resembles a CRUD (Create, Read, Update, Delete) approach.

Advantages

  • Simple for clients – they only retrieve and store data without needing to understand domain logic.
  • Fewer endpoints with a consistent URL structure.
  • Well-aligned with REST principles.

Disadvantages

  • Business logic can be difficult for clients to understand and discover.
  • Risk of logic being spread across clients if the API does not provide enough guidance.
  • Less suitable for complex processes involving multiple steps or data types.

Example

GET /data/submission
GET /data/submission/1199930
POST /data/submission
PATCH /data/submission/1199930

When is this approach suitable?

  • For simple systems where processes are not highly complex.
  • When clients primarily work directly with data (e.g., case handlers).
  • When minimal coupling between clients and domain-specific business logic is desired.

2. Object-Oriented Approach

Description

In this approach, each resource is treated as an object that has both data and associated operations (methods). Clients can not only retrieve and update data but also trigger specific actions on each resource. This makes business logic more explicit in the API.

Characteristics

  • Each resource has its own set of operations/actions.
  • Clients must understand domain-specific concepts and processes.
  • The approach resembles object-oriented systems, where objects have methods.

Advantages

  • Clearer process support – clients receive explicit signals about available actions.
  • Easier for clients to navigate business logic.
  • Well-suited for resources with many specific actions governed by business rules.

Disadvantages

  • Can lead to an explosion of endpoints when multiple resources have multiple actions.
  • Maintaining a consistent structure across various object types can be challenging.
  • Can become cumbersome if many actions are not resource-specific but apply across multiple resources.

Example

POST /data/submission/search
POST /data/submission/submit
POST /data/submission/1199930/selectPractice
POST /data/submission/1199930/cancel

When is this approach suitable?

  • When resources have specific, business-related operations.
  • When it is important for clients to understand the processes around resources.
  • When the API is part of a larger domain application with domain-oriented users.

3. Action/Process-Driven Approach

Description

This approach explicitly separates actions from data. Clients retrieve and manage data in one way, while business processes and operations are modeled as separate process resources or services. This allows actions to involve multiple data types simultaneously and handle more complex workflows.

Characteristics

  • Clear distinction between data and processes.
  • Processes have dedicated endpoints that handle multiple resources and complex logic.
  • Suitable for larger, cross-cutting processes.
  • Often inspired by Command-Query Responsibility Segregation (CQRS).

Advantages

  • High flexibility in modeling business logic.
  • Easier to version or modify process logic without changing data models.
  • Well-suited for systems with complex, multi-step workflows.

Disadvantages

  • Can create uncertainty about which data the processes operate on.
  • Requires more documentation and client adaptation.
  • May result in an artificial separation of data access and process handling, even when logically connected.

Example

POST /process/submitReimbursementClaim
POST /process/updateReimbursementClaim
POST /search

When is this approach suitable?

  • When processes involve multiple different data types.
  • When processes have high complexity and multiple steps.
  • When processes should function as “black box” operations with clear input and output.
  • When supporting both manual and automated workflows via the same interface.

Summary Evaluation

ApproachClient SimplicityFlexibilityProcess SupportSuitable for Complex Domains
Data-Driven✅ Very simple❌ Limited❌ Weak❌ Not well-suited
Object-Oriented⚠️ Moderate⚠️ Moderate✅ Good⚠️ Partially suitable
Action/Process-Driven⚠️ Requires learning✅ High✅ Very good✅ Highly suitable

Recommendation

Choosing an approach should be based on:

  • The complexity of the domain.
  • How self-sufficient clients need to be.
  • How clearly processes need to be defined for clients.
  • Whether the API is primarily a CRUD interface or a process-driven system.

In many cases, a hybrid model may be the best solution, where basic data is managed using a data-driven approach, while more complex workflows are exposed via process-driven endpoints. This provides both simple data handling and flexible process support.