Once when I was six years old I saw a magnificent picture in a book, called True Stories from Nature, about the primeval forest. It was a picture of a boa constrictor in the act of swallowing an animal. ... In the book it said: "Boa constrictors swallow their prey whole, without chewing it. After that they are not able to move, and they sleep through the six months that they need for digestion." I pondered deeply, then, over the adventures of the jungle. And after some work with a colored pencil I succeeded in making my first drawing. My Drawing Number One. It looked something like this:
xxxxxxxxx xxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I showed my masterpiece to the grown-ups, and asked them whether the drawing frightened them. But they answered: "Frighten? Why should any one be frightened by a hat?" My drawing was not a picture of a hat. It was a picture of a boa constrictor digesting an elephant. But since the grown-ups were not able to understand it,...

The Little Prince, Chapter 1 by Antoine de Saint-Exupéry

I must be the snake the little prince was thinking of because I seem to be in the habit of swallowing elephants. I often find myself needing to learn a complex system inside and out very quickly. Sometimes, it is because I have a new client. Especially in my early days I would often get invited to lead the design on a half finished project after a series of the big guys (Accenture, etc) had successfully botched the project. Until then, companies don't really look beyond the obvious and safe sources of consultants. More recently, I have either needed to evaluate 3rd party software systems for in-house use or even our own code for refurbishment. And then there are volunteer projects.

Over the course of time I've evolved a strategy for working through complex systems rather quickly. I won't say it isn't a lot of work (it is), but it keeps me from going in endless circles, so that at least the work moves me forward.

I'd be interested in knowing more about how other monks handle going about learning a new system quickly. Not everyone learns the same way. Also if you see any non sequiturs or an obvious omission, I'd appreciate the feedback. Writing something like this up feels a bit like trying to explain how to tie a shoe. It is all to easy to take for granted a crucial step and leave it out. I also have an ulterior motive. pmdevils are also in the position of trying to swallow elephants when they join.

The ten steps can be summarized (they will be explained in more depth later below):

  1. Gather together whatever documentation there is
  2. Experiment with the front end
  3. Study permanent data streams and persistant data
  4. Explore the class/type systems
  5. Understand where the code lives
  6. Scan and categorize the codebase
  7. Study back end CRUD operations
  8. Study front end CRUD operations
  9. Study permissions/security infrastructure
  10. Explore a well defined functional area to see how the system works in a specific case.

This is, of course, an interactive process and it often involves a lot of backtracking as well. Often the first pass through a step results in more questions than answers. Then I go onto another step, and find an answer to something I couldn't figure out in the previous step. If the answer is important enough, I may go back and redo all or part of the previous step.

Although the first step is "gather together documentation", the process describe below will work even if documentation is sketchy and you have to manually go through code and database schemas. Documentation makes the process much easier (if it is correct), but it isn't necessary to the learning process.

Figuring out how much detail to do at any one step of the process is a bit of an art. Usually I try to be as superficial as possible. For each stage I try to learn enough to categorize things in some sensible way that tells a story about data, behavior or the interaction between them. Then I move on to the next step. When the fog to understanding ratio gets out of hand, I backtrack one or two steps and go into more detail until the fog clears.

As I work through the above list I usually keep copious notes and organize them as I go. Writing out my answers to the questions at each step helps me identify unanswered questions. I also find I'm accumulating knowledge much faster than I can absorb it so the notes act as a memory bank, especially if they are well organized. I frequently organize and reorganize the notes as I go. The act of organizing them also helps me remember more. Finally, if the system is poorly documented, the notes become a first cut at improved documentation.

As one skims through the list, the first thing that might stand out is that a third of the steps are data centric, including the first detailed analysis step (study data streams and persistant data). For many programmers starting with data may seem counter intuitive. Programming is about making data do something, and code is where the action is. But data puts an upper bound on what the code can do so it provides a way of focusing attention and understanding the scope of the system. It is also the easiest discreate nameable thing to get a handle on. It acts like the end of a thread used to unravel a ball of knots.

Another thing that might stand out to the OO fans reading the list above is that there is no mention of roles or behaviors or all the other jargon of the OO world. I find that interesting because I've been doing OO since the late 80's and it is virtually impossible for me to design software that is more than 500 lines without ending up having at least a few classes. Even before C++ became popular I was organizing C functions associated with specific data structures into dedicated files and designing dispatch handlers for the data structures. It just seemed like the right way to do things.

I'm guessing the reason for this is that data is a long hand that reaches throughout a system. Or maybe it would be better described as blood and oxygen. Every aspect of the system needs it regardless of its role front end or back end.

Classes and objects are marvelous ways to organize code. They can also be a great way to get a user to cough up requirements. Users often have a hard time talking about data apart from the things they do with the data. However, code and end-user requirement gathering are only two of the many ways a system needs to be categorized, sliced and diced in order to understand it. On the front and we need to understand workflows. On the database we need to understand normalization or we won't really get the benefit of our database's SQL engine. Somewhere in between we need to understand aspects: large swaths of functionality that are content independent.

To fully understand a system it is also important to get a handle on how all of these different ways of categorizing data, code, and end user functionality relate to one another. The learning strategy elaborated below tries to help in that process. Whether it succeeds is for you to decide. But if it seems counter intuitive at least consider trying it the next time you need to learn a system quickly. You might be surprised at the results.

I apologize in advance for the list like nature of this elaboration. Partly I don't have the time to expand it fully right now. Also, I fear turning it into a narrative with examples would likely stretch this node to book length. Hopefully though I will have at least raised some questions and pointed out things to look for that might be helpful for others.

Step 1: Gather together documentation

You may have documentation. You may not. It may be up to date. It may not.

Even out of date documentation or incorrect documentation can be helpful if it gives you a sense of the design philosophy or a road map through the code. My first step is to skim through the documentation and make some sort of assessment of what is there and how much I trust it. However, unless the documentation is amazingly clear and well written, when first learning a system I usually take the documentation with a grain of salt. I like to get down and dirty into the guts of things and see how it all works with my own eyes.

Even when I trust the documentation and it seems relatively complete I still use the remaining steps to as a road map through the documentation and check list for my understanding of the system.

Step 2: Experiment with the front end

From the user's point of view, what does the system do? Do this as a brief survey to get a "feel" for the application or system. The goal for this step is to provide context for the more detailed study of how the system is implemented.

Step 3: Study permanent data streams and persistant data

Step 4: Explore the type systems

Step 5: Get to know where the code lives and how it is processed

Step 6: Scan and categorize the codebase

If you are lucky a certain amount of categorization may have been done for you. If not, the only way to do this is to look at each file! Here are some things I do to speed up the process:

Step 7: Study backend CRUD operations

Step 8: Study front end CRUD operations

Taking the same types as before, study how those types are displayed to the user.

Step 9: Study permissions/security infrastructure

Step 10: Study a well defined data collection/functional area

Nuf said.

Best, beth

Replies are listed 'Best First'.
Re: Swallowing an elephant in 10 easy steps
by fullermd (Priest) on Aug 14, 2009 at 03:08 UTC

    As one skims through the list, the first thing that might stand out is that a third of the steps are data centric, including the first detailed analysis step (study data streams and persistant data). For many programmers starting with data may seem counter intuitive. Programming is about making data do something, and code is where the action is.

    On the contrary, this is exactly how I approach things.

    After all, data is where the action is; code is just transforming one set of data into another. The data you start with and the data you want to end up with are what determines what code you need. And, a data model (not necessarily the data model you encode into your database or file format or whatever) is a proxy for the customer needs as well.

    The first question I ask (of myself, if not necessarily of other people) on any project is "what data are we dealing with?" You can't figure out what the code needs to do until you know how the data will be organized, and you can't figure out how to organize the data until you know what data you have. Of course, you can't figure out how your data can best be organized until you know what you'll do with it to, so it's totally a back-and-forth process between data and process, but understanding of both preceeds code.

    Everybody Knows that you need both data and process understanding to figure out how to organize everything, and that they each impact the other, so you have to get both together and let them hammer out the necessary compromises. But, IMO, any given person thinks of one or the other first, and then applies the other to it.

    I feel like this is one of those dichotomies by which you can classify types of programmers. Not that it reveals anything vital by the classification, but at least it helps you communicate across the gap by realizing it's there. And it doesn't mean that you necessarily think your primary grabbing point is the more important. I'm very much a data guy, but I'm well aware that the processes are vital things to understand to build the data model. I just start with the data itself.

      After all, data is where the action is.
      As the famous quote goes:
      The programmer at wit's end for lack of space can often do best by disentangling himself from his code, rearing back, and contemplating his data. Representation is the essence of programming.
      from The Mythical Man-Month.

      After all, data is where the action is.

      As the famous quote goes:

      Show me your code and conceal your data structures, and I shall continue to be mystified. Show me your data structures, and I won’t usually need your code; it’ll be obvious.
      from The Cathedral and the Bazaar.

Re: Swallowing an elephant in 10 easy steps
by jdporter (Canon) on Aug 13, 2009 at 18:00 UTC

    Why not just assign ten people to the task, one step per person?

    oh, wait...

    Nevermind. :-)

Re: Swallowing an elephant in 10 easy steps
by biohisham (Priest) on Aug 13, 2009 at 17:18 UTC
    And this caught me wondering about the time constraints, how long can it take to walk along these 10 steps?I know this is more of a general question, but what I mean is, the average of the time taken from you while setting to work on say ...about 5 projects where you had to involve this strategy...

    Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind.

      The time drivers are the overall quality of the design, ease of access to code and database schemas, and the size of the system: the number of database tables, the complexity of the type/class system(s), the amount of code, and the number of features in whatever subsystem you explore in step 10. Rather than an average, I'll take the most recent example, Perl Monks.

      The Perl Monks website has 83 data tables, two main type hierarchies (nodetypes and perl classes), a core engine of about 12K and about 600 additional code units spread throughout the database. Documentation is scattered and mostly out of date. The initial architecture seems solid but its features have been used inconsistently over time. Accessing the schema and code samples is slow because there is no tarball to download - it has to be done through the web interface or manually cut and pasted into files off line. The database/class assessment (1-4) took about 16 hours. Steps 5-7 took about 30 hours. Steps 8-10 took about 24 hours. All told that is 70 hours, including writing up documentation and formatting it with HTML.

      However, I always like to leave myself some breathing space. If I were contracting to learn a system that size, I'd want 90 hours and an opportunity to reassess time schedules after the initial code walk through was complete. If a system is very poorly designed this process takes somewhat longer.

      A crucial element in controlling time is controlling the amount of detail needed to gain understanding. It is easy to lose sight of the forest for the trees. That is why I advise stopping and moving onto the next phase once your categories give a place to most design elements and the categories work together to tell story. That is also why I recommend backtracking as needed. Sometimes we make mistakes about which details really matter and which can be temporarily blackboxed. Knowing I can backtrack lets me err on the side of black boxing.

      The other element affecting time is, of course, the skill of the analyst or developer. I have the advantage that I have worked both at the coding and the architecture level of software. I doubt I could work that fast if I didn't know how to read code fluently and trace the flow of data through code. Having been exposed to many different system designs over the years also helps - architectural strategies leave telltale footprints and experience helps me pick up on those quickly.

      However one can also learn these skills by doing. The more you practice scanning, categorizing and tracing through code and data the better you get at it. It will take longer, but the steps are designed to build on themselves and are, in a way, self-teaching. That is why you can't just do the 10 steps in parallel as jdporter jokingly suggests below.

      However some theoretical context and a naturally open mind definitely helps: if you think that database tables should always have a one-to-one relationship with classes you will be very very confused by a system where that isn't true. If I had to delegate this work to someone else I probably would work up a set of reading materials on different design strategies that have been used in the past 30 years. Alternatively or in addition, I might pair an analyst with a programmer so that they could learn from each other (with neither having priority!)

      Best, beth

      Update: expanded description of the PerlMonks system so that it addresses all of the time drivers mentioned in the first paragaph.

      Update: fixed miscalculation of time

        I haven't see the PerlMonks code, but I have seen the perl source and read much of the documentation. I wonder if you have studied it, how you would tackle it and what reasonable time line expectations might be for gaining an overall understanding of it.

Re: Swallowing an elephant in 10 easy steps
by MadraghRua (Vicar) on Aug 14, 2009 at 19:35 UTC
    Nice post. I use a variation on this figuring out how to develop, assume control of or modify apps for scientists. Just out of curiosity, how much time do you spend talking to people? from your writing that is the one difference I would have with you - I usually find documentation to be sketchy at best. When I've done these types of projects I usually try to make time to talk to the developers, the dbas, IT support, the end users. Sometimes you can eliminate a lot of work with a couple of questions. And I've also found some of my interpretations based on analyzing code base were wrong - I corrected this by again looking at the data flow and asking the developers & users what was going on in that portion of the application. Of course if you're taking over a botched project or a closed app, most of this conversation is with the end users and IT support for the system.

    yet another biologist hacking perl....

      The amount of time spent talking with/listening to people varies with the project and the availability of people. I tend to have two very different kinds of conversations: (a) requirement oriented conversations focused on goals and priorities (b) deeply technical conversations focused on understanding the system and finding the least disruptive way to meet those goals.

      Getting a sense of goals and priorities before one starts such a large analysis project is an absolute must! Even if I and my team are the end users, it is still a good idea to explicitly clarify our goals and expectations one to another. When we share a work environment and culture it is all too easy to make assumptions.

      The goal setting conversations can strongly affect the choices made during the formal technical analysis. The process described above gives one an overview. I generally know what data there is and how it flows, but I have only studied a few of the subsystems in depth. Having a sense of goals is especially helpful in step 10, where I choose a subsystem (and sometimes more than one) to focus in on. In earlier phases knowing goals helps me keep an eye out for (a) hidden resources in both code and data that could support goals but aren't being used (b) missing resources that must be added. (c) possible bugs or extension difficulties in existing features. I usually try to keep notes as I go.

      Requirements conversations tend to happen primarily before even beginning the process and then again at the end, when "what next" decisions need to be made. They can also happen midway, if the analysis process seems to be taking more time than originally expected. I always like to give people a chance to reevaluate priorities. I'm more likely to get paid for my time if the client is the one calculating the trade-off between goals, time, and money.

      Deep technical conversations tend to happen during the process described above. First, in some cases there are no people. If I'm reviewing an open source package for its range of features and customization support, documentation, code, and database schemas are all I have to go on. If it is poor or sketchy then the process takes longer because I have to reconstruct more.

      Even when there are people, I need to keep in mind two important constraints: Many people, including technical people, are much better at doing their job than explaining it. Even if they are good at explaining, they have limited time to assist in the more detailed phases of analysis. The more I can get on my own, the happier they are.

      During step 1, the initial documentation phase, I look for "oral documentation" and try to assess the level of team knowledge about their own system. Knowledge of one's own system can deteriorate over time. In a complex system, most people are only asked to work on a small part of it. Team culture may not encourage them to look outside of their own box. It may be easier to hard code data in a function than go through the bureaucracy involved in adding a new field to a database table. This eventually results in patches and enhancements that don't quite fit with the original design and even the original authors of the system may no longer fully understand how the system works. But when the oral documentation is good, that is wonderful and it can indeed save hours of time.

      I also like to understand how the development and maintenance teams function. Is there an overall philosophy? Has it changed over time? Do they have a formal structure to enforce that philosophy? Or do they rely on organic methods? Does it work? Why or why not? Do they read their own documentation? If not, why? When they do, does it help? What would make it more helpful? This kind of information gives me context when I see a design with two competing architectures for the same aspect (e.g. two different permissions management infrastructure).

      At the end of each step, I ask the existing team to review my work. This helps catch misunderstandings, especially in the earlier steps where I haven't yet connected up the code and data. I'm constantly trying to control the level of detail and sometimes I make incorrect snap judgments based on the way things are named. If that can be caught early on by an experienced team member, so much the better. As you have said, understanding data flow is a crucial tool in correcting mistakes. How data is used is just as important as what it is called and how it is arranged (though sometimes these are in conflict). In the early stages, data flow knowledge isn't there yet, though sometimes I will try a quick grep through the code to see if it sheds any light on an oddly named field.

      While going through each step, I keep a list of unresolved questions. I pick the most important questions and ask the existing team how they might answer them. I find that people seem to find it much easier to talk about how a system works when they are asked about a specific part of it. Even design philosophy discussions tend to be more informative and exact if I can point to two specific examples that challenge the assumption of a single philosophy.

      Best, beth

Re: Swallowing an elephant in 10 easy steps
by biohisham (Priest) on Aug 14, 2009 at 21:06 UTC
    Over the past few hours, I have been trying to identify with these 10 steps. The suggestions and ideas you graciously shared are reflection-inducing. Starting off, these ten steps are like beads to a string, I had the notion that this is so much of a teaching-material quality, because you have addressed two interleaving concepts, the Database issue and the Programming one. So seriously consider having this published and displayed on a wider scale to those whose interests fall inside these concerns. I mean after having duly taken into accounts the many suggestions and inspirations that would pour.

    within the points 3-6, the area where scanning, categorizing and tracing is more crucial, Exploring the type system can by far be one of the toughest takes especially while exploring the code structure and the classes relationship on one hand and on the other if a database is involved, the records types. The flow between classes and the events that trigger these classes, the associated variables and scopes are all places where an attention has to be sharp, refined, present, so without a clear understanding of the data streams and the way things go, and how these data travel through classes and code back and forth with the combined ability to visualize them in that act, a correlation between these two steps can be illusive.

    As I said I have been trying to identify with these gems, It appears to me that you have a programming style that is -if I may say- based on characterization, for something to be cognizable enough it has to have this character to it serving as a dimension with every involved character behavior pretty much as in a story, interleaving, associating, working together... but I am failing to reach to figure how I can read the plot from the code and where the main knot of it is.

    With regard to the time constraints, you have dealt with fairness at addressing the time requirements and the spaces while bearing in mind the iterative nature of the process. That is an admiration of skills I have for you, I look up to this level of stability. :).. Thanks for sharing this with us...

    Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind.
      but I am failing to reach to figure how I can read the plot from the code and where the main knot of it is.

      The metaphor of story may not work for everyone. If I were to deconstruct the concept of story, I would point to two key features of well-written stories:

      • Every detail of the story has a place. There is nothing superfluous. Everything plays a role in developing either the plot or the characters or the fundamental message (a.k.a. theme) of the story.
      • It is possible to step back from the story and construct a narrative to explain the role of each detail in the story.

      A good choice of categories for database tables lets us assign a role to each table. Once we have a set of roles we can try to construct a narrative to explain how each of those roles works together to create a whole system. The question is: have we created a well-written story?

      In particular, our narrative needs to explain how the categories we've chosen give the front end the data it needs. We also need to explain what functionality could explain the data that has no obvious connection to the front end and how it may related to the goals of the end-user view of the system. Maybe it helps performance? Supports development? Represents related, but unused features? Solves an architectural problem related to system design? Sometimes it is obvious what kind of functionality this extra information might support. If it isn't, we can put them aside for a bit and hope that maybe their purpose will be clearer when we see whether or not the code uses these tables, and if so how.

      However, if we find more than a few things we can't categorize or our categories don't seem to fit together, then we also need to take a hard look at the way we are categorizing.

      According to psychologists who specialize in memory and cognition, experts and non-experts differ significantly in the way they use categories to organize information:

      • experts organize information using more categories than non-experts
      • these categories tend to reflect deeper principles rather than superficial features.
      • these categories are cross referenced to each other in ways that make it very fast for experts to retrieve information and apply it to new problems in their area of expertise.

      For example, a novice might try to group database tables based on structural features like primary key or the presence of blob fields. However, she is likely to find it very difficult to use these categories construct a narrative explaining what she saw on the front end because the categories are too superficial. Since she can't compose a narrative, she has to rethink her categories. If she is lucky, trying to construct the narrative to explain how the data supports the front end might hint at another more meaningful way of categorizing tables. If not, a smart novice will find someone with more experience and ask them how they might organize the tables. Sometimes we do need to learn by watching the example of others. If there are no handy experts, she might try one of the approaches here or here to get her creative juices going.

      The goal of the emphasis on story is to help one question one's categories until they are deep enough to be useful. The iteration and interleaving of code and data and focus on flow are also meant to help enrich the connections between categories and make it easier to retrieve information when you need it. A nice summary of the role schemas (categorization systems/stories) play in expert knowledge can be found here.

      Here also some links to research on expert knowledge for software programming and development. I found a few interesting observations here and there about the role of data and code but none of these seemed to say much about learning systems whole.

      • Expert Programming Knowledge: a Schema-Based Approach - dense review of research literature, 2007. One conclusion was that research results may depend on what language the programmer was used to programming in. COBOL programmers apparently found it easier to understand data relationships than Fortran programmers.
      • Roles of Variables in Teaching suggests that experts cluster a lot of tacit knowledge around the various roles variables play in programs. Teaching that explicitly may help students learn to build better programs.
      • Applying Cognitive Load Theory to Computer Science Education has a few speculative comments towards the end about the role schemas might play in reducing cognitive load. Apparently there isn't a lot of research on how schemas can reduce information overload in programming.

      Best, beth

      Update:in process, to be added: links to articles on expert schema research specifically in the field of computer programming added.

      Update: revised research links to describe information in each article relevant to understanding systems and data/code relationships.

      Update: added links to monkish brainstorming of what to do when you are really stuck trying to understand something.

      Update: clarified the deconstruction of "story".