
ASP.NET MVC is Microsoft’s latest entry into the world of web application development. It was introduced in 2009 as an alternative to ASP.NET Web Forms.
Microsoft’s evangelists have positioned ASP.NET MVC as next big step in web development. Maybe it is. However, to keep it in perspective remember that MVC is just a design pattern. ASP.NET MVC is Microsoft’s implementation of the MVC design pattern for serving up web pages. MVC stands for Model View Controller. A better ancronym would be MVC-R as Routing is an essential part to Microsoft’s implementation.
ASP.NET MVC and ASP.NET Web Forms take very different approaches to achieve the same goal. Neither is a panacea. In this article I will compare and contrast both frameworks and enable you to make an educated decision as to which to use.
First, we’ll go over how ASP.NET Web Forms and ASP.NET MVC process a request from a Web Browser. Next, we’ll look at the major design patterns used by each framework. After that we’ll work through a couple of common programming scenarios. Finally, we’ll sum up the pros and cons of each framework.
Okay, so let’s get started…
How Requests Are Processed
Let’s get started by describing how each framework processes a request from the Web Browser. Keep in mind that what follows is by no means comprehensive and is just a profile of the default processing inherent to each framework…
ASP.NET MVC
The ASP.NET MVC processes requests from a Web Browser like this …
- The Web Browser submits a request to IIS. IIS passes the request to the ASP.NET pipeline where it is processed by the Router.
- The Router interprets the request and passes it on to the appropriate Controller.
- The Controller retrieves the appropriate data for the request. The data is referred to as a Model.
- The Controller retrieves the appropriate View for the request.
- The Controller binds the View to the Model to generate an HTML document. This document is sent back to the Web Browser.

The ASP.NET MVC Request Lifecycle
ASP.NET Web Forms
In comparison, ASP.NET Web Forms processes requests from a Web Browser like this …
- The Web Browser submits a request to IIS. IIS passes the request to the ASP.NET pipeline.
- The ASP.NET pipeline passes the request to the appropriate Page.
- The Page retrieves the appropriate data for the request and generates an HTML document. The document is sent back to the Web Browser.

The ASP.NET Web Forms Request Lifecycle
Similarities and Differences
In ASP.NET MVC a Controller determines what should be returned to the Web Browser. The Controller binds a View and a Model together to create an HTML document. There is a one-to-many relationship between a Controller and the Views and Models available to it. As such a Controller can produce many different types of HTML documents.
In comparison, an ASP.NET Web Forms Page consists of server side code (.aspx.cs) and HTML markup (.aspx). The code binds data to the markup to produce an HTML document. There is a one-to-one relationship between a Page’s markup and it’s code. A Page can only produce one type of HTML document.
In ASP.NET MVC data is encapsulated into a Model. Only one Model can be bound to a View. ASP.NET Web Forms has no such restrictions. A Page can be bound to multiple data sources.
Design Patterns
There are three core design patterns used by ASP.NET Web Forms and ASP.NET MVC. These are the MVC Pattern, the Front Controller Pattern, and the Page Controller Pattern. When you think “MVC” you should think of the Front Controller and MVC patterns. When you think “Web Forms” you should think of the Page Controller pattern.
Model View Controller
The MVC pattern describes how the entities of ASP.NET MVC relate and interact with one another.
The Model does not know about the View or Controller. The Model just exposes data. The Controller knows about the View and Model. This is because the Controller needs to be able to retrieve data from the Model and return the approrpiate View. The View only knows about the Model. This is because the View needs to be able to bind to the data exposed by the Model. The View never makes requests against the Controller.
Requests are always made against the Web Server, which passes it on to the Router, which passes it on to the proper the Controller.

The Model View Controller (MVC) Pattern
Front Controller
ASP.NET MVC uses the Front Controller pattern to determine how to handle requests from a Web Browser.
In a Front Controller there is no direct mapping between a request and a resource. Instead, the request is interpretted by the Front Controller and routed to the appropriate resource.
In ASP.NET MVC the Router acts as a Front Controller. Requests made to it are interpretted and routed to the appropriate Controller. The Controller also acts as a Front Controller. When a request is routed to it by the Router it determines the appropriate View to return to the Web Browser.

The Front Controller Pattern
Page Controller
ASP.NET Web Forms uses the Page Controller pattern to determine how to handle requests from a Web Browser.
In a Page Controller there is a direct mapping between a request and a resource. The Web Browser’s request corresponds directly to a resource. There is no interpretation.
In ASP.NET Web Forms the Web Forms Page being requested is the Controller – and it only knows how to return itself.
Keep in mind that it is plausible for an ASP.NET Web Forms application to use a routing mechanism like the one used in MVC. In this scenario the application would use the Front Controller pattern and the Page Controller pattern.

The Page Controller Pattern
Code Samples
I have created a sample ASP.NET Web Application called Notepad for the sake of this article. It implements both an MVC version and a Web Forms version of a simple note taking interface. Each version implements a synchronous Form POST for retrieving Notes and an asynchronous Form POST for adding new Notes. By evaluating each version of Notepad we can get a better idea of the differences between Web Forms and MVC…
ASP.NET MVC Form POST
Performing a Form POST in ASP.NET MVC is suprisingly straightforward if you ignore everything you learned about ASP.NET Web Forms.
The MVC Notepad has a Form for searching authors. Submitting the Form performs a POST of the Form’s data against a URL. ASP.NET Routing evaluates the URL and routes the request to the appropriate Controller. The Controller retreives the desired Notes, binds them to a new View, and returns the resulting HTML document.
ASP.NET Routing is configurable. This is what the Route looks like for the “Search By Author” Form in the MVC Notepad.
routes.MapRoute( "MvcGetNotesByAuthor", "Notepad/MvcGetNotesByAuthor", new { controller = "Notepad", action = "GetNotes" } );
Global.asax.cs in ScottsJewels.Samples.Notepad
And this is what the “Search By Author” Form looks like.
<form action="<%= ResolveUrl("~/Notepad/MvcGetNotesByAuthor") %>" method="post"> <div> <fieldset> <input type="text" id="searchAuthor" name="searchAuthor" /> <button type="submit" name="button" value="GetNotesByAuthor">Search Author</button> </fieldset> </div> </form>
/Views/Notepad/Notepad.aspx in ScottsJewels.Samples.Notepad
When the Form is submitted the action of the Form is compared to the Routes mapped in ASP.NET Routing. In this case the “GetNotes” function is called on the “Notepad” Controller. Somewhat confusingly ASP.NET MVC implies that the class name for the Controller is “NotebookController” (the Controller part is tacked on automatically). The “GetNotes” function is referred to as an ActionMethod. This is what the “GetNotes” ActionMethod looks like.
[HttpPost] public ActionResult GetNotes(string button, string searchAuthor) { ActionResult result; switch (button) { case "GetNotesByAuthor": if (searchAuthor != null && !string.IsNullOrEmpty(searchAuthor.Trim())) { result = View("Notepad", NotepadDataAccess.GetInstance().GetNotesByAuthor(searchAuthor)); } else { result = View("Notepad", NotepadDataAccess.GetInstance().GetNotes()); } break; default: result = IndexNote(); break; } return result; }
/Controllers/NotepadController.cs in ScottsJewels.Samples.Notepad
The ActionMethod accepts parameters. The parameters are implicilty mapped to the request’s payload by name. In this scenario the “button” parameter will contain the value of the HTML button that submitted the form.
The ActionMethod also returns an ActionResult. There are various types of ActionResults that you can return from a ActionMethod. In this scenario we are returning a ViewResult. The ViewResult binds a View and a Model together to create an HTML document. This particular ViewResult is binding a list of “Note” Models to the “Notepad” View.
The [HttpPost] attribute decorating the “GetNotes” ActionMethod restricts it to HTTP POST requests. If desired you can restrict your ActionMethod to specific HTTP verbs such as GET, PUT, and DELETE using similar attributes.
This is what the “Note” Model looks like.
[Serializable] public class Note { [Required] [DisplayName("Author")] public string Author { get; set; } [Required] [DisplayName("Title")] public string Title { get; set; } [Required] [DisplayName("Text")] public string Text { get; set; } }
Note.cs in ScottsJewels.Samples.Notepad.DataModels
And this is what the “Notepad” View looks like.
<%@ Page Title="My Notepad (MVC)" Language="C#" MasterPageFile="~/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<ScottsJewels.Samples.Notepad.DataModels.Note>>" %> <%@ Import Namespace="ScottsJewels" %> <%@ Import Namespace="ScottsJewels.Samples.Notepad.DataModels" %> ... <asp:Content ContentPlaceHolderID="bodyContent" runat="server"> ... <div id="notes"> <% if (Model != null && Model.Count > 0) { foreach (Note note in Model) { %> <div class="note"> <span><%=note.Title%> by <%=note.Author%></span> <p> <%=note.Text%> </p> </div> <% } } %> </div> </asp:Content>
/Views/Notepad/Notepad.aspx in ScottsJewels.Samples.Notepad
Notice the Page directive at the top of the View specifies which type of Model the View can be bound to. A View can only be bound to a single Model type. For complicated Views requring multiple data sources this might require composite ViewModels to be created. Also, notice that there is no code-behind (.aspx.cs) for an ASP.NET MVC View. All of the logic takes place in either the View’s markup or the Controller. When this View loads it will display all of the “Note” objects contained within it’s bound Model.
ASP.NET MVC Form POST using AJAX
In ASP.NET MVC there are two ways to handle an AJAX Form POST. You can either expose a Web Service. Or, you can leverage an ASP.NET MVC Controller. A Web Service is more familiar and allows you to set up a standalone “data tier” (with a little bit of work). However, if you want to adhere to Microsoft’s implementation of MVC you should probably use a Controller. Web Services do not fit very well into the MVC pattern. In addition, Web Services can not be routed to by ASP.NET Routing and will result in a Resource Not Found (404) error.
For the sake of this example I am going to implement AJAX using the ASP.NET MVC Controller.
This is what the “Add Note” Form looks like.
<form action="<%= ResolveUrl("~/Notepad/AddNote") %>" id="addNoteForm"> <div> <fieldset> <legend>Add Note</legend> <p> <label for="author">Author :</label> <input style="position:absolute; left: 120px;" type="text" id="Author" name="Author"/> </p> <p> <label for="title">Title :</label> <input style="position:absolute; left: 120px;" type="text" id="Title" name="Title"/> </p> <p> <label for="text">Text :</label> <input style="position:absolute; left: 120px;" type="text" id="Text" name="Text"/> </p> <p> <button type="submit" name="button" value="GetNotes">Add Note</button> </p> </fieldset> <div id="message"></div> </div> </form>
/Views/Notepad/Notepad.aspx in ScottsJewels.Samples.Notepad
At first glance it may seem like clicking the “Add Note” button will perform a synchronous POST to the the Web Server. However, if you take a look at the client-side Javascript you will see that the Form’s submit event has been overridden. Instead of performing a synchronous POST the Form will be submitted asynchronously using JQuery’s .ajax() method.
$(document).ready(function () { $("#addNoteForm").link(jsonNote); $("#addNoteForm").submit(function (event) { // Stop form from submitting normally. event.preventDefault(); var $form = $(this); var $message = $("#message"); var url = $form.attr('action'); $('#addNoteForm').unlink(jsonNote); $.ajax({ type: 'POST', contentType: "application/x-www-form-urlencoded", data: jsonNote, url: url, success: function (data) { $form.link(jsonNote); if (data.IsSuccessful == true) { $message.text("Note added successfully!"); jsonNote.Author = ""; jsonNote.Text = ""; jsonNote.Title = ""; $form[0].reset(); } else { $message.text(data.ErrorMessage); } $message.fadeIn(500,function () { $message.fadeOut(2000); }); } }); }); });
/Scripts/Notepad.js in ScottsJewels.Samples.Notepad
The Javascript blocks the default behavior of the submit event and extracts the Form’s information from the HTML DOM. It then populates a JSON representation of the “Note” Model and submits it to the Web Server. Where did the “Note” come from? It was rendered to the View when the following Javascript was processed by ASP.NET. For more information on how to do this (and why) check out my previous post.
<script type="text/javascript" language ="javascript"> var jsonNote = <%= (new Note().ToJson() ) %> </script>
/Views/Notepad/Notepad.aspx in ScottsJewels.Samples.Notepad
This is what the Route looks like for the “Add Note” form. Routing treats all requests the same regardless if they are synchronous or asynchronous.
routes.MapRoute( "AjaxAddNote", "Notepad/AddNote", new { controller = "Notepad", action = "AddNote" } );
Global.asax.cs in ScottsJewels.Samples.Notepad
The URL requested by the “Add Note” Form is mapped to the “AddNote” ActionMethod on the “Notepad” Controller. The “AddNote” ActionMethod looks a little different than the one used in the ASP.NET MVC Form POST example. For starters it returns a JSONResult – not a View. JSONResult is just that – a JSON result, or pure data. It also accepts a “Note” object as a parameter. The parameter is bound to the requests payload with a little bit of help from JQuery.
[HttpPost] public JsonResult AddNote(Note note) { JsonResult result = new JsonResult(); if ((!string.IsNullOrEmpty(note.Author)) && (!string.IsNullOrEmpty(note.Title)) && (!string.IsNullOrEmpty(note.Text))) { NotepadDataAccess.GetInstance().SubmitNote( new Note { Author = note.Author, Text = note.Text, Title = note.Title } ); result.Data = new ClientResponse(true, true, string.Empty); } else { result.Data = new ClientResponse(false, false, "Author, Title, and Text must not be empty!"); } return result; }
/Controller/NotepadController.cs in ScottsJewels.Samples.Notepad
How to Link the Form’s Fields
The JSON “Note” fields are linked to those on the Form using JQuery DataLinking. Datalinking is a new JQuery Plugin that allows you to link a Javascript object to a DOM field using the .link() and .unlink() methods. Keep in mind that DataLinking is still in Beta and still has the occasional hiccup. For example, when performing an .ajax() post you need to make sure that you .unlink() your Javascript object or JQuery will throw a nasty error.
Content Type Limitations of the Controller
An MVC Controller does an admirable job mapping a request’s payload to an ActionMethod’s parameters. However, it doesn’t support all HTTP content types. Suprisingly, JSON which is quickly becoming an intergral part of web development due to it’s small footprint and easy integration into client-side Javascript – is not supported! Naturally, there are a couple of workarounds. You could create a custom binding mechanism which does support JSON. A Controller’s parameters are bound to the request’s payload by the DefaultModelBinder. By implementing and registering a new version of IModelBinder you can supplement the default binder and add support for additional content types. Or, you could just leverage JQuery…
JQuery implicitly converts your payload for you. JQuery’s .ajax() method allows you to specify a “data” parameter and a “contentType” parameter. Typically, if I were performing a JSON post to a Web Service I would specify a “contentType” of “application/json; charset=utf-8” and supply a JSON object for “data”. However, given the Controller’s JSON limitations we can specify “application/x-www-form-urlencoded” instead. JQuery is smart enough to convert the JSON object to a name/value pair compatible with a Form POST.
$.ajax({ type: 'POST', contentType: "application/x-www-form-urlencoded", data: jsonNote, url: url, success: function (data) { ... } });
/Scripts/Notepad.js in ScottsJewels.Samples.Notepad
ASP.NET Web Forms Form POST
An ASP.NET Web Forms Page can be integrated into your ASP.NET MVC web application. ASP.NET Routing allows you to Route a URL to a Page just as easily as a Controller. MapPageRoute() allows you to map a URL request to an ASP.NET Web Forms Page. This Route configures a URL request for “Notepad/WebFormsNotepad” to invoke the WebFormsNotepad.aspx Page for a response.
routes.MapPageRoute( "WebFormsNotesIndex", "Notepad/WebFormsNotepad", "~/WebFormsNotepad.aspx" );
Global.asax.cs in ScottsJewels.Samples.Notepad
The Page contains a server-aware Form for retrieving “Notes”. The Form contains a server-aware Button named “getNotesByAuthor”. The Button specifies a server-side event handler named “OnSearchAuthor” to handle OnClick events. When the Button is clicked a POST will be submitted against the Web Server for “NotePad/WebFormsNotepad”. The POST will contain data for the server-aware Form. ASP.NET Routing will route the request to the WebFormsNotepad.aspx Page. The OnLoad event of the Page will then fire followed by the “OnSearchAuthor” method.
<form runat="server"> <div> <fieldset> <asp:TextBox ID="searchAuthor" runat="server" /> <asp:Button ID="getNotesByAuthor" OnClick="OnSearchAuthor" runat="server" Text="Search Author"/> </fieldset> </div> </form>
WebFormsNotePad.aspx in ScottsJewels.Samples.Notepad
public partial class WebFormsNotepad : System.Web.UI.Page { protected List<Note> Model; protected void OnSearchAuthor(object sender, EventArgs e) { if (searchAuthor.Text != null && !string.IsNullOrEmpty(searchAuthor.Text.Trim())) { Model = NotepadDataAccess.GetInstance().GetNotesByAuthor(searchAuthor.Text); } else { Model = NotepadDataAccess.GetInstance().GetNotes(); } } ...
WebFormsNotePad.aspx.cs in ScottsJewels.Samples.Notepad
This is what the rest of the Page’s markup looks like. As you can see it looks very similar to it’s ASP.NET MVC View counterpart. This was done intentionally on my part to simplify the comparison to ASP.NET MVC. Keep in mind that an ASP.NET Web Forms Page is a very different beast than an ASP.NET MVC View. Although not shown in “Notepad” a Page can be bound to multiple data sources of various types. A Page also has vastly superior server controls. These server controls expose a rich server side coding experience and can preserve state through the ViewState.
<%@ Page Title="My Notepad (Web Forms)" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="WebFormsNotepad.aspx.cs" Inherits="ScottsJewels.Samples.Notepad.WebFormsNotepad" %> <%@ Import Namespace="ScottsJewels" %> <%@ Import Namespace="ScottsJewels.Samples.Notepad.DataModels" %> ... <asp:Content ContentPlaceHolderID="bodyContent" runat="server"> ... <div id="notes"> <% if (Model != null && Model.Count > 0) { foreach (Note note in Model) { %> <div class="note"> <span><%=note.Title%> by <%=note.Author%></span> <p> <%=note.Text%> </p> </div> <% } } %> </div> </asp:Content>
WebFormsNotePad.aspx.cs in ScottsJewels.Samples.Notepad
ASP.NET Web Forms Form POST using AJAX
In a ASP.NET Web Forms Application there are two ways to handle an AJAX Form POST on the Web Server. You can either expose a Web Service. Or, you can expose a Web Method on the Page itself.
This is where it gets interesting.
If you host a Web Form Page within an ASP.NET MVC Application you have the ability to handle the AJAX Form POST using an MVC Controller. This allows us to leverage the code base from the previous ASP.NET MVC Form POST using AJAX example. If you think about how this works it’s pretty simple. The AJAX Form POST requests for a resource. The resource is mapped to a Controller by ASP.NET Routing. The Form does not care what processes it’s request – only that a JSON response is returned. The Controller does not care about where the request came from – just how to handle it.
And this is where it gets really interesting.
By leveraging the Controller and client-side Javascript of the ASP.NET MVC Form POST using AJAX example there is very little left to do. This is what the “addNoteForm” Form looks like on the Web Forms Notepad. Submitting it will POST to the same URL as the MVC Notepad’s “addNoteForm” Form. ASP.NET Routing will route the request to the same Controller and ActionMethod used by the MVC Notepad. The ActionMethod will return a JSONResult. Neat, huh? We’re all done!
<form action="<%= ResolveUrl("~/Notepad/AddNote") %>" id="addNoteForm"> <div> <fieldset> <legend>Add Note</legend> <p> <label for="author">Author :</label> <input style="position:absolute; left: 120px;" type="text" id="Author" name="Author"/> </p> <p> <label for="title">Title :</label> <input style="position:absolute; left: 120px;" type="text" id="Title" name="Title"/> </p> <p> <label for="text">Text :</label> <input style="position:absolute; left: 120px;" type="text" id="Text" name="Text"/> </p> <p> <button type="submit" name="button" value="GetNotes">Add Note</button> </p> </fieldset> <div id="message"></div> </div> </form>
WebFormsNotePad.aspx in ScottsJewels.Samples.Notepad
Content Type Limitations of a WCF Service
In all honestly my reasons for using an MVC Controller over a Web Service or Page Web Method hides some underlying problems. I was unable to get a Page Web Method to work within the confines of ASP.NET Routing and kept getting a Resource Not Found (404) error. I had a little more luck when using a WCF Web Service except that it was unable to parse the Form data in the request. Previously I stated how an ASP.NET MVC Controller is unable to parse JSON content but can parse Form content. Well, ironically WCF is capable of the exact opposite. Bummer. Rather than reinvent the wheel and introduce some new JSON-specific Javascript I decided to “go with it” and leverage what was already available – the MVC Controller.
Pros and Cons
No comparison would be complete without a list of the Pros and Cons of each technology, right? At first glance it may seem that ASP.NET MVC is vastly superior simply because it has more Pros and less Cons when compared to ASP.NET Web Forms. This is not the case. The items in the following list should be weighted based upon three things …
- Initial complexity of the web application.
- Desired scalability of the web application.
- Your skillset.
Only after taking these factors into consideration should you make a judegment call on which framework to use.
ASP.NET MVC
Pros
- Good design is baked-in. MVC promotes good design out-of-the-box. It forces you to group similar functionality into entities whereas each entity has as little overlap as possible. This is known as Seperation of Concerns. Specifically, ASP.NET MVC forces you to separate your presentation (the “View”), business logic (the “Controller”), and data (the “Model”) into separate classes.
- Easier testing. It is easier to test components when they do not rely upon one another. Dependencies suck.
Scalability. By extension good design and testability make an application more scalable.
- Elimination of ViewState, Server Controls, and Postbacks. ASP.NET MVC forces you to adhere to the stateless nature of HTTP with the elimination of these faculties. A lack of ViewState decreases the usefulness of the “rich” Server Controls used in ASP.NET Web Forms. As such ASP.NET MVC relies heavily upon client-side development using Javascript and JQuery. ASP.NET Web Forms allowed you to hook up events for control events that would trigger a postback to the server. In ASP.NET MVC there is no such thing as an event-driven postback. All posts in ASP.NET MVC must occur from within an HTML Form and utilize the standard HTTP GET and POST verbs.
- Supports multiple HTML Forms. An ASP.NET MVC Controller can support POST’s from multiple HTML Forms. An ASP.NET Web Form Page can only support POST’s from a single (server-aware) HTML Form.
Cons
- Unnecessary complexity. Not all web applications need to be scalable. Sometimes you just need a very simple application.
- Steeper learning curve. Need to be savy in client-side development using Javascript and JQuery in order to maintain rich client experience.
- Lackluster support for JSON. ASP.NET MVC doesn’t like JSON too much. An MVC Controller can return JSON but cannot receive it. This puts ASP.NET MVC at a disadvantage when using AJAX as all requests containing JSON content must be converted.
- Elimination of ViewState, Server Controls, and Postbacks. Why is this also a con? Because in all honesty these faculties do allow you to develop quicker.
ASP.NET Web Forms
Pros
- Easier to transition from Windows Forms style development. Windows development is stateful and event driven. In comparison HTTP is stateless and form based. The Web Form’s ViewState makes web development stateful. Furthermore, Web Form Server Controls simulate event driven development through Postbacks.
- Large 3rd Party Support. Rich drag-and-drop controls are offered from many companies such as Telerik, Infragistics, etc…
Cons
- No abstraction between an HTTP Request and a Page. By default an HTTP Request maps directly to a Page. URL Rewritting and Routing (which provide such an abstraction) are not baked-in. Microsoft has made MVC’s routing mechanism available to Web Forms in ASP.NET 4.0.
- Larger page footprint. The ViewState can be a hog. Preserving state on a Page increases the page size. ASP.NET 4.0 has exposed new ways to control the ViewState. For example, the Control.ViewStateMode property now allows you to disable ViewState for an entire Page and subsequently enable it for just the controls that require it.
Subject to Poor Design. Unless your ASP.NET Web Forms application is designed properly it lends itself to a tighly coupled architecture where data access, presentation, and business logic are all merged into a Page’s code behind.
Difficult to Test. Testing requires spooling up the entire ASP.NET runtime to support the ViewState, Postbacks, and Server Control rendering.
More difficult client-side Javascript and CSS. Server control tags are not always rendered appropriately. ASP.NET 4.0 has exposed new ways to control tag rendering. For example, the Control.ClientIDMode property provides more control over how ID’s are generated.
Conclusion
Phew! Well there you have it – a comparison of Microsoft’s premier web development frameworks.
Feel free to download the sample Notepad source code here.
If you have any questions, comments, or suggestions please let me know.
#.NET #MVC #Webservices #ASP.NET #Programming #SoftwareDevelopment #SoftwareEngineer #DrawnAndCoded