Category Archives: programming

A system for analyzing systems [Part 2]

This is the second part of an (awesomely epic) series on how to analyze a software system quickly, click here to view Part 1.

So now you (having the right attitude and documenting everything) have viewed the main nouns (data) and verbs (use cases, workflows).  What do you do next? Profit?

#3 Find integration points and environmental concerns

It is important to understand the system within its software ecosystem – find the system edges:

  • Make a list of the technologies used (again – avoid judgment)
  • Make a list of the integration points (other systems, off the shelf products, web service apis, manual steps in overall process)
  • Look at dll references, build files, web references, configuration files, database connection strings, etc.
  • Draw a simple sketch of the application within its context with others

#4 Read some code

This is normally where developers start – reading semi-colons.  But maintain some discipline:

  • Make a simple outline of the project structure and main namespaces and entry points. Input, processing, output.
  • Try to not go down every rabbit hole too quickly – add to your WTH list liberally and then circle back.
  • Guided by what you know already dive in based on what makes sense – read the billing module, the calculation piece, the ranking algorithm, or the integration with Facebook.
  • Document system conventions, code conventions, and architecture. The build order can be useful and painfully revealing here – on larger projects I break out ndepend to see the overall structure.

#5 Read some documentation

If the project has some artifacts now is a good time to read them. Most people will read them earlier in the process but since documentation is a record of what was the truth during a time in which the truth was still being figured out when people had time to do such things I’ve grown untrusting of it until I know what is what in the application. Project documents are fascinating in that they provide historical clues to design decisions, project status, pressure, and onetime events like data conversions and client meetings that affect the system in the future. Read up and take notes. Be aware that some pieces of documentation are position papers and do not represent the current reality.

#6 Talk to some people

Armed with nouns, verbs, and a basic understanding of the code go get your questions answered by someone on the project (if they will return your calls). A few tips for this process if a developer is handing off to you:

Realize that when a developer hands off they will do two things:

  • Blow off steam (I really didn’t want to do this but that freakshow client forced us to)
  • Play defense / apologize (I was under pressure so just hacked this up)

Let this happen, avoid judgment and be friendly and helpful.

#8 Identify the Win Condition

By this stage in the game you should know the “win condition” of the system – when it works how it provides value to a customer.  Examples would be:

  • “Allows users to find out about flight delays before other airlines, saving them time and allowing them an advantage against other travelers in a cancellation scenario” (FlightCaster)
  • “Provide an easy to use way to update online ads across multiple platforms based on your inventory” (Tentail)
  • “Allows users to find out about current events much faster, trade cat pictures, and waste half of their life” (Twitter)

#8 Wrapping up

So now you have your document with just Facts and Questions having hopefully whittled down your WTH list. Polish up your facts and add a summary statement and the application “win condition” to the top and you are done:

EasyTrack is a web-based accounting system used by small businesses that is built on ASP.NET Web forms with a SQL Server 2005 database. It integrates with Quickbooks, exports transactions as csv, and has a web service api for 3rd party integration. It syncs nightly with a client customization of an older version that uses the same database but has a non-web frontend. It works in Internet Explorer. Its niche is being an easy to use tracking system for a discrete set of common accounting tasks that are harder to do with larger systems.

A system for analyzing systems [Part 1]

We seek definition to understand the system so that we can discern
the rules so that we know what to do next so that we win.

— Michael Lopp, Being Geek: The Software Developer’s Career Handbook

How do you approach learning a new system that is dumped on your lap like a spilled plate of nachos? Let’s say somebody walks up to you and says:

Hey nerd- we need you to look at the EasyTrack system tomorrow – we have a buglist that we need you to churn through before the end of the week. Ralph was working on it but after his nervous breakdown he won’t return our calls.

As developers we are asked to jump into complex software systems all the time and a big part of our value is simply understanding systems (not just changing them or making new ones). This is a skill that we need to be intentional about. We need a system for learning systems. Here is mine.

First steps: Attitude

Avoid judgement of the people who have worked on the system, the client’s goals, the choice of typeface on the one page of system documentation, the developer’s skills and experience, the organization of the code, the choice of technology, the choice of coffee used during the project, the organization of the altar built to the stored procedure that does everything, et al.

You have no idea the in-the-womb conditions on the project and don’t know the real trade-offs that were in play. Like many negative emotions judgement will close you down and cloud your thought so keep a clear mind when reviewing a system. Starting with a good attitude will also help you in talking to people on the project later on.

First steps: Document

As you work through the steps document what you are finding. When I do this my goals are:

  • To make me feel like I’m doing something as just staring at a new system can be brutal.
  • To make a deliverable that I can publish at the end of the system analysis if needed.
  • To allow me to have a place to store questions that I can’t find out from just reading code.

On that last point – I normally have Facts, Questions, and WTH sections in the document that I use as I work through a system. Facts are things that I have learned definitively about the system, Questions are open questions that I have researched and can’t find an answer to without asking someone or gaining access to something else, and WTH are bogeys – items that I see that I don’t understand that I’m deferring until later.

An example – as I’m reviewing the database I see an oddly-named stored procedure: spSyncOrderNoTransactionWithRollback? Hmm… So we add this to the WTH list until we can go back and read through it, where it is called, etc.  So we might end up with:

WTH

  • In spSyncChangeOrderNoTransactionWithRollback - how can you have a rollback without a transaction – is this nested?

Which leads later to the following:
Facts

  • Some of the stored procedures exist in the database but aren’t used by the application and are part of an export / sync process.  They start with spSync*
  • Some, but not all, of the stored procedures use nested transactions with checkpoint.

Questions

  • What is the export/sync process called by job execSync4_Bak that runs every night and calls the spSync* sprocs? [No access across linked server to the tables it is writing to]

As I work through the system I’m simply turning WTHs into Facts or Questions. The temptation once you have a basic understanding of the system is to stop typing – don’t do this.

If you maintain the facts list and polish it up you’ll have a nice system overview document. The easiest time to document a system is when it doesn’t feel like you are having to write-up up a bunch of things you already know.  So by writing while learning the system you can trick yourself into producing nice documentation that is much easier to keep updated later.

#1 Start with the data, don’t start anywhere else

I didn’t say start with the project plan, system documentation, email threads, interviews, test plans, or insulting notes about the system on the bathroom wall. The live data is the only thing that you can be sure is actually real in the beginning.

Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowcharts; they’ll be obvious.

— Fred Brooks, The Mythical Man-Month: Essays on Software Engineering

I’ve found this quote to be true – your code follows your data and you will get further faster if you understand the basic nouns in the application first. Seeing an Order table with a StatusId field gets you along the road faster than trying to read a 2,000 line method:

1
void placeOrder(int? productId, int priceStructureId, decimal price, int priceSigDigits, int userKey, string userNameInternational, bool isA, object? requiredBlob, out long intRetVal1, out long intRetVal3, out bool multi)

So go ahead:

  • Create database diagrams to identify relationships
  • Begin drinking if there are no enforced relationships
  • Look at table sizes to identify transactional vs. configuration tables
  • Look at meta and programmable database constructs – sprocs, functions, jobs, indexes, linked servers, etc.
  • Begin thinking through the possible workflows – an Order table with OrderDetail which points to Product and PriceStructure implies that in the application someone is purchasing something and that prices vary.

#2 Use the application

If at all possible get a login to the application to view it as a customer does. You can’t always do this safely but you should try. Developers are always saying that the UI is the tip of the iceberg and that all the logic is in the back-end, but knowing the data model and having poked around the application will teach you the vocabulary of the system very quickly.

  • What are the main uses cases [verbs] in the application?
  • What nouns/entities that we saw in the data model are actually shown to the user?
  • Is there data shown to the user that wasn’t in the data model? [hint for the next step]
  • What doesn’t appear to be working or isn’t intuitive? Enjoy this perspective as once you learn the rest of the system you’ll lose it forever.

Tune in for the exciting – no electrifying – conclusion to how to analyze a system for fun and profit.

Glorious Hack #97: Wrapping regular ViewResults as JSON for rich client-side processing

I recently had the need on a ASP.NET MVC/js project to wrap the results of a controller action in JSON so the middle-class UI could consume and then do one of:

  • Redisplay the current active popup because validation failed (and I wanted to use the built-in MVC validation)
  • Close the current popup and refresh a grid on the parent page with the new item
  • Display a generic error message page (in the case of something unexpected like an EndOfWorldException being thrown (by who?))
  • Display an even nicer error message page with contextual information returned from the controller about the data entity

Logic like this seems to want to occur on the server side – a combination of ActionResult and some logical branching would normally work but in the context of a rich javascript UI and the current context being an active jQuery dialog box that won’t work – the user won’t like a full page refresh, and you can’t tell the javascript which ViewResult you’ve returned of the above cases.

So I came up with the following which is either a glorious hack or just a hack:

  • Have the controller return JsonResult in all cases with a instance of a simple wrapper type ValidationResultViewModel
  • Have the javascript then decide what to do with the payload based on this information
  • Generate payload by running the ViewResult via your controller and making the payload a regular string

So the ValidationResultViewModel class looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ValidationResultViewModel
{
        public bool IsValid { get; private set; }
        public bool IsError { get; private set; }
        public string Payload { get; private set; }
        public long EntityId { get; private set; }
 
        public static ValidationResultViewModel Make(bool isValid, bool isError, string payload, long entityId)
        {
            return new ValidationResultViewModel
            {
                IsValid = isValid,
                IsError = isError,
                Payload = payload,
                EntityId = entityId
            };
        }
}

This class is populated by the controller’s action like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected JsonResult PartialViewToValidationJson(string viewName, object model, bool isValid, bool isError, long entityId = 0)
{
	string payload = RenderPartialViewToString(viewName, model);
 
	var viewModel = ValidationResultViewModel.Make(isValid, isError, payload, entityId);
	return this.Json(viewModel, JsonRequestBehavior.AllowGet);
}
 
protected string RenderPartialViewToString(string viewName, object model)
{
	if (string.IsNullOrEmpty(viewName))
		viewName = ControllerContext.RouteData.GetRequiredString("action");
 
	ViewData.Model = model;
 
	using (StringWriter sw = new StringWriter())
	{
		ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
		ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
		viewResult.View.Render(viewContext, sw);
 
		return sw.GetStringBuilder().ToString();
	}
}

There is a bit of magic here – we are rendering the view as the engine would run it and then transforming its payload back into a string to return to the client.  These methods live on a BaseController class and reuse ViewData et al. from the existing context.

This is not a normal approach – ViewResult and the way it works are abstracted to allow unit testing and separation of concerns, etc.  We are reusing this to say that we want to force it to render as we want to tradeoff some performance on the server for some magic on the UI.

So now your controller is free to populate it as it sees fit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[HttpPost]
public virtual ActionResult Edit(SomethingViewModel viewModel)
{
	if (!ModelState.IsValid)
		return this.PartialViewToValidationJson(MVC.Something.Views.Basics, viewModel, false, false);
 
	try
	{
		Something.Save(viewModel.ToSomething(), this.User.Username);
		var something = Something.GetById(viewModel.SomethingId);
		viewModel.Populate(something);
		return this.PartialViewToValidationJson(MVC.Something.Views.Basics, viewModel, true, false);
	}
	catch (Exception e)
	{
		return this.PartialViewToValidationJson(MVC.Shared.Views.Error, ErrorViewModel.Make(e), false, true);
	}
}

Think russian dolls – in your controller action you are essentially calling another controller action, rendering it, then making it a string and returning it as part of your returned result:

-> popup -> js does a $.post to Something/Edit -> using existing Model renders Something/Basic as a string -> makes ValidationViewModel -> returns as JsonResult

The client can then do things like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$.ajax({
	async: false,
	cache: false,
	context: form,
	type: form.attr('method'),
	url: form.attr('action'),
	data: form.serialize(),
	success: function(data) {
		if (data.IsValid) {
			successTarget.replaceWith(data.Payload);
			ret = true;
		}
		else if (data.IsError) {
			if (data.Payload) {
				errorTarget.find("form").replaceWith($(data.Payload));
			}
			ret = false;
		}
		else {
			// replace only form contents to prevent overriding
			// any .dialog or other post load changes to target
			if (data.Payload) {
				errorTarget.find("form").replaceWith($(data.Payload).find("form"));
			}
			ret = false;
		}
	}
});

I actually ended up replacing this with generic callbacks so that you could pass in what the UI should do in the event or an error condition, success, validation failure:

1
2
3
4
5
6
7
8
9
10
11
success: function(data) {
	if (data.IsValid) {
		validationSuccessCallback(data);
	}
	else if (data.IsError) {
		errorCallback(data);
	}
	else {
		validationFailureCallback(data);
	}
}

And we have extended it a bit to include other client-side view model type data.

A hack, but one that fits the tradeoffs on the table – sacrifice some performance on the server side and add some complexity for the payoff of a nice wicked fast client UI experience.