Adam Konrád, Stack Overflow Blog https://stackoverflow.blog Essays, opinions, and advice on the act of computer programming from Stack Overflow. Wed, 17 May 2023 13:26:08 +0000 en-US hourly 1 https://stackoverflow.blog/wp-content/uploads/2017/03/cropped-SO_Logo_glyph-use-this-one-smaller-32x32.jpg Adam Konrád, Stack Overflow Blog https://stackoverflow.blog 32 32 162153688 Keep ‘em separated: Get better maintainability in web projects using the model-view-controller pattern https://stackoverflow.blog/2023/05/17/keep-em-separated-get-better-maintainability-in-web-projects-using-the-model-view-controller-pattern/ https://stackoverflow.blog/2023/05/17/keep-em-separated-get-better-maintainability-in-web-projects-using-the-model-view-controller-pattern/#comments Wed, 17 May 2023 14:00:00 +0000 https://stackoverflow.blog/?p=22178 MVC is an old pattern, but it's still relevant to web apps.

The post Keep ‘em separated: Get better maintainability in web projects using the model-view-controller pattern appeared first on Stack Overflow Blog.

]]>
From a user’s perspective, a software application, whether it’s mobile, desktop, or web based, is something that you interact with and it does stuff. But from a developer’s perspective, there’s a lot of separate actions that occur: the user interacts with a UI, the program takes that input and performs some tasks on the data, and the data feeds back into the user interface. That’s the technical version of doing stuff.

Software engineers use a design pattern to build this separation of concerns into their software architecture. The model-view-controller (MVC) was created in the late 1970s by Trygve Reenskaug when working on Smalltalk. Though it is an older, pre-web design pattern, it is ubiquitous in modern applications. The pattern can be found on the web in both backend and frontend applications, on mobile, desktop, and more. 

The pattern’s name references models, views, and controllers. These are the core building blocks in MVC apps. Each of these components has a specific role and behavior. Let’s take a closer look at the use and implementation of this pattern.

The basics of the pattern

Models provide access and storage for the application data. Typically a model represents a database table. However, models can represent data in any form, they may represent data held in memory, cache, or even a third-party service. Models implement methods to access or modify the data, most commonly these are create, read, update, and delete (aka CRUD) operations.

Views transform the application data in memory to user interface elements. They may also just serialize the data or transform the data to other user interface representations like the native user interface in mobile apps.

Controllers are the glue of the pattern. They are the entry point of the pattern and make models and views work together. All operations are first handled by controllers. Controllers offer high-level actions with required data inputs. Upon an action call, they handle action on the data, pass the data to the models, and then pass the models or the data to views for rendering. All user actions in the application are passed through the controller. The controller decides what should happen next.

MVC on the web

In web applications, controllers handle HTTP requests and responses. They are responsible for pulling the right data from the requests, performing the business logic with error handling, and returning the correct HTTP status and data in the HTTP response. Controllers are typically stateless and own the stateful instances of the models and views. In web applications, controllers create model and view instances when a request comes in or when an action is called, but they don’t own or call other controllers.

Models are responsible for holding the state of the application. In other words this means models store and do low-level work with the data, it can be a class or an object with methods and members holding data or it can be a wrapper of a third-party API. Models frequently have an asynchronous API because they work with low-level asynchronous operations like working with the database, reading from a file, or communicating over a network. Models can work with other models, call them, and use their methods. One model may create composite and more complex operations based on the functionality of other low-level models. As an example, a model may pull some data using another model that wraps a third-party service and store that data into a database table using a model for that table. Models should not work on their own. They don’t call controllers or views or hold any references to them.

Views render the data in a web browser or mobile application. Web application views are stateless and they can be as simple as JSON serializers or as complex as HTML templating engines. Views take data in the form of model instances or custom objects and translate the data into the requested form. Sometimes web applications can support multiple forms of data representation driven by the HTTP Accept header. In MVC, this could just mean employing a different serializer for JSON or XML or a templating engine for the data. Views may call other views to render different types of objects or sub-objects. Views may receive model instances and work with model APIs. However, patterns where a view would directly manipulate data in the model is not recommended. It is always and only the responsibility of the controller to call functions on models that could manipulate the state of the application. Avoid the possibility of this pattern by creating a proxy object that holds the data ready to be rendered in the controller, initializing these objects with data from the models and passing this proxy object down to a view. This way, the controller stays in control of the view’s access patterns and data exposed to the view.

Interfaces

Each component of the MVC design pattern has an interface. This interface provides a way to interact with the objects. In an MVC application on the web, this interaction starts with the controllers. A web server typically interacts with a controller after it receives an HTTP request. The requests are first processed by lower levels of framework or web server logic. This processing handles parsing and deserializing the request and calling a method on the controller.

The web MVC controllers typically implement methods that represent operations provided by the HTTP protocol: GET, POST, PUT, and DELETE. But these HTTP methods don’t need to map to a single method on the controller. The mapping is controlled by routing logic that provides additional options to invoke the right controller method based on the paths or parameters provided in the request URL. In an app using a database, some controllers might implement methods like list or index to wrap model calls and display lists of records with their ids and then a get method to get a specific record based on its id.

For retrieving records, the call comes from controllers to models. In web application frameworks like Ruby on Rails or Django, you may find models providing or implementing several find methods. These methods accept a record id and or other parameters that are passed to queries to lookup specific records. To create records, the models implement create factory methods or allow instancing the model. The models are instanced via class constructor and their properties are set through the constructor parameters or through setter methods defined on the model. The models sometimes wrap specialized record creation into class or static factory methods. These factory methods can provide a nice interface allowing you to quickly understand how the model is instanced in the application code or in tests.

Implementation pitfalls

The most common pitfall when implementing new MVC apps is the lack of respect towards the separation of concerns presented and enforced by the pattern. Developers sometimes decide to cut corners and skip the separation of concerns in order to be quickly done with their changes.

Too smart views

The views in MVC web applications are responsible for translating internal data structures into presentable text format like XML or HTML. Some logic is necessary to do so. You can iterate over arrays of records using loops. You can switch between different types or sections of content for display using branching. Views by nature contain a large amount of markup code. When the markup code is mixed with looping and branching logic, it is much harder to make changes to it and visualize the results. Too much branching or conditions in the view also make the markup hard to read. Spaghetti only works as food, not code. There are two tools that help with this problem: helper functions and view file splitting.

Helper functions are a great way to isolate logic that may be called multiple times, shared, or re-used between views and let you follow DRY principles. Helpers can be as simple as providing an element class name based on some input or as complex as generating larger pieces of the markup.

File splitting is another powerful approach that can improve code readability and maintainability. Every time a view grows too large or too complex, you can break it down to smaller pieces where each piece does only a single thing. Just the right amount of view file splitting can make a big difference in organization of the project.

Overapplying helper functions or view file splitting can also lead to problems, so it is best to drive changes when there’s a reason—and that reason should be a too complex view.

Heavy controllers

Controllers are responsible for implementing actions coming from the frontend, the UI, or the API of the application. For many developers, it becomes tempting to implement data processing logic in the controllers. Sometimes you may see database queries built directly in the controllers and the results stitched together with other data in some complex logic and then the results returned to the views or serializers. Other instances of this problem look like third-party API calls made directly from controllers and the results adjusted or mixed with other data from the local models and returned back to the client. Both of these are cases of heavy controllers.

The controllers should be light on logic. They should process input parameters and pass the adjusted input down to the models. The controllers take the inputs, choose and call the right models and their methods, retrieve the results, and return the results in the right format back to the client. Heavy lifting with data shouldn’t be done directly in the controller methods to avoid code complexity problems and issues with testing.

Light models

Models are built to manage, hold and represent the application data. Every time the application needs to work with the data, it should reach into some model. Sometimes application developers decide to perform data manipulation in controllers, which makes the controller code more complex and creates code maintenance problems. Since models work with data, any incorrect logic on the models may corrupt the data. This is why the models should have the highest test coverage out of the entire app. Well-implemented models are very easy to unit test. 

Application logic that reads and writes data should be concentrated on the related models. For example, if the data queries are spread all over the controller code then it is harder to get good model test coverage. When the developers debug issues like slowness in the database, they may not see all queries performed against a table right away so it may take them longer to optimize the queries and implement better indexing.

Another example can be an interaction with third-party services. Applications frequently require communication with cloud and data providers and this is done through asynchronous calls and APIs. These methods return data that gets processed, altered and saved in the application. It is beneficial to treat these interactions with third-party APIs as model logic and place it on the respective model. This puts the third-party logic to a specific place in the app where it can be quickly covered with integration tests.

Conclusion

The MVC pattern was developed to separate concerns and to keep the application code highly organized. Frameworks like Ruby on Rails that are built on top of this pattern and reap the benefits of strict naming and placement. Closely following this pattern makes projects based on it easier to understand, maintain, and test. Correct usage of its rules reduces developers’ mental load and speeds up any bug fixing or new feature development. Long live the model-view-controller!

The post Keep ‘em separated: Get better maintainability in web projects using the model-view-controller pattern appeared first on Stack Overflow Blog.

]]>
https://stackoverflow.blog/2023/05/17/keep-em-separated-get-better-maintainability-in-web-projects-using-the-model-view-controller-pattern/feed/ 8 22178
You want efficient application scaling? Go serverless! https://stackoverflow.blog/2020/05/18/you-want-efficient-application-scaling-go-serverless/ https://stackoverflow.blog/2020/05/18/you-want-efficient-application-scaling-go-serverless/#comments Mon, 18 May 2020 13:58:56 +0000 https://stackoverflow.blog/?p=15913 Today we’re seeing another shift from virtual machines to containers. Containers are virtual runtime environments running on top of the operating system kernel that emulates the operating system itself. That’s where the serverless model comes in.

The post You want efficient application scaling? Go serverless! appeared first on Stack Overflow Blog.

]]>
In the first decade of the 2000s, we saw a shift from using physical hardware to virtualization. This shift was driven by increases in clock frequencies and number of cores in modern CPUs. It was no longer cost effective to run a single application on a single physical server. Both open source and commercial hypervisor solutions started gaining tremendous popularity in the server market.

Today we’re seeing another shift from virtual machines to containers. Containers are virtual runtime environments running on top of the operating system kernel that emulates the operating system itself. The difference between virtual machines and containers is the virtual machines emulate hardware and containers—running on top of a VM—emulate just the operating system. 

Operating system emulation has numerous benefits. Processes running in containers are isolated from each other. Containers can be started and terminated quickly as their footprint or size is typically minimal, making them a great choice for fast scaling.

Virtual machines and containers allow software to better utilize purchased hardware. Multiple applications can run concurrently on the same hardware. For finance departments, high utilization means effective use of capital expenditures.

That’s where the serverless model comes in. Serverless computing describes an application architecture designed as a collection of managed cloud services. Managed services like AWS Lambda, Aurora, or CloudFront provide high added value and allow you to build your applications quickly. You spend your time focusing on developing your business logic and not fiddling with infrastructure, like making sure your database operates as a cluster. The serverless application model helps you spin up your application stack and easily organize and manage the state of cloud resources associated with your application.

Serverless computing was made possible by significant technological advancements in container space. Cloud providers striving to reduce overhead and allow fast provisioning of resources in their platforms quickly found out it was necessary to develop new types of hardware accelerated hypervisors.

The combination of hardware-accelerated hypervisors like AWS Nitro and virtual machine monitors like AWS Firecracker allowed fast deployment and execution of container environments. Thanks to this new generation of hardware accelerated hypervisors, compute services like AWS Lambda now offer much higher CPU, disk, and network I/O performance.

A cost-effective cloud environment is all about high utilization of provisioned resources. Serverless computing promises that you’re always running your applications at 100% utilization. When running your applications on serverless, your provisioning is always perfect because you only pay for machine time you actually used. 

Serverless compute scales well with your incoming traffic compared to classic compute services like EC2. Scaling with classic EC2 typically involves another service called auto scaling. Auto scaling itself is a complicated feature that handles tracking select metrics like CPU load or network utilization of your virtual machines. Auto scaling uses these metrics to trigger alarms to scale your EC2 instance fleet up or down. As you can tell, EC2 fleet scaling involves a lot of steps, and it’s really easy to miss something because every application is quite different.

With serverless, you can forget all that complexity. Serverless application scaling literally just copies the final container to select hypervisors, executes it, and routes all incoming events to it. If your serverless application fits into container size and runtime memory limits, you’re good to go and your application will scale just right. This is enabled by containers and hypervisors that run closer to hardware. Serverless has very little to no operating system overhead because it’s running just kernel code and the application in its containers.

Scaling is something serverless excels at, but it also has some limits. Serverless is a perfect compute model for event-based processing. Most workloads on the web are event-based or can be converted to it. Currently, serverless doesn’t work well with long-running processes because AWS Lambda and other cloud services have execution limits. Classic VM compute or other types of container-based compute are still better suited for long running processes. As long as your workload can be event-driven and processing an event is relatively quick with low resource requirements, serverless should work well for you. None of the current serverless limits seem to be permanent and service offerings should develop and improve over time. A limit that’s been a problem yesterday may not even exist tomorrow.

Finally, the most important thing: cost. How much cheaper is it to develop and run serverless applications? I’d say it depends on the application. When your applications scale well, they will provide better value for you and your users. You’ll run them cheaper. Serverless helps by eliminating overprovisioning and overpaying for compute resources you don’t utilize. While serverless improves scaling, other billable utilization still applies—you should still think about how much bandwidth, storage, and database volume you use. Serverless will not help you better utilize those resources at the moment, that’s still up to you and your application.

You may have read posts around the internet about serverless and vendor lock-in. I’d say you can still control those aspects in the architecture of your applications. Serverless computing has numerous benefits—great performance, easy setup, and quick development and deployment. This is why serverless became an important tool in my toolbox for building high performance applications that scale well while maintaining low cost. If you haven’t tried serveless yet, you should. Start by exploring the serverless application model.

Editor’s note: For those curious about the serverless offerings outside of those mentioned in this article, check out AWS Lambda, Azure functions and Google Cloud Functions. h/t to Chris in the comments.

The post You want efficient application scaling? Go serverless! appeared first on Stack Overflow Blog.

]]>
https://stackoverflow.blog/2020/05/18/you-want-efficient-application-scaling-go-serverless/feed/ 26 15913