Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage Articles Real-World Rule Engines

Real-World Rule Engines

This item in japanese

For many developers, rule engines are buzzwords, or black boxes on an architectural diagram: something to be feared or admired from afar, but not understood. Coming to terms with this, is one of the catch-22s of technology:

  • It's difficult to know when to use a technology or how to apply it well until you've had some first-hand, real-world experience.
  • The most common way to gain that experience is to use an unknown technology in a real project.
  • Getting first-hand experience using a new technology in a production environment is an invaluable experience for future work but can be a major risk for the work at hand.

Over the course of this article, I'll be sharing my practical experience with rule engines and with Drools in particular to support in-market solutions for financial services, in order to help you understand where rule engines are useful and how to apply them best to the problems you face.

Why Should I Care?

Some of you will have already considered using a rule engine and will be looking for practical advice on how to use it well: patterns and anti-patterns, best practices and rat-holes.

Others haven't considered using a rule engine, and aren't sure how this is applicable to the work you're doing, or have considered rule engines and discarded the idea. Rule engines can be a powerful way to externalize business logic, empower business users, and solve complicated problems wherein large numbers of fine-grained business rules and facts interact.

If you've ever taken a series of conditional statements, tried to evaluate the combinations, and found yourself writing deep nested logic to solve a problem, these are just the sorts of entanglements that a rule engine can help you unravel.

Some of our more complicated financial services work, when rephrased in a rule approach, began to look markedly more comprehensible. Each step in converting procedural conditional logic to Drools business rules seemed to expose both more simplicity and more power at once.

Finally, if you're not convinced by the above, consider this: rule engines are a tool, another way to approach software development. Tools have their strengths and weaknesses, and even if you aren't making immediate use of this one, it's helpful to understand the tradeoffs so that you can assess and communicate applicability in the future.

A Rule Engine Primer

Before we can really talk about when and how to use rule engines, it's worth explaining what rule engines are.

What's a Rule Engine?

A rule engine is, at its core, a mechanism for executing 'business rules'. Business rules are simple business-oriented statements that encode business decisions of some kind, often phrased very simply in an if/then conditional form.

For instance, a business rule for a hypothetical insurance system might be:

Given a car and a driver, if all of the following conditions are met:
  • The car is red
  • The car is in a sport class
  • The driver is male
  • The driver is between the ages of 16-25
The consequence is a 20% insurance premium increase.

These business rules are not new: they are the business logic that is the core of many business software applications. If you're a developer, you've seen these rules time and again expressed as a subset of requirements. They're statements like "give a twenty-percent discount on orders over three items on Mondays" or "we don't insure sixteen-year-old males on supersport motorcycles."

The primary difference with a rule engine is in how these rules are expressed; instead of embedding them within the program, these are encoded in business rule form. This form varies from rule engine to rule engine.

Rule engines are not limited to execution; they often come with other tools to manage rules: common options allow the creation, deployment, storage, versioning and other such administration of rules, either individually, or in groups.

One example of a rule engines is Drools, which has recently been brought under the banner of the JBoss group. Because Drools is freely available, is open-source, and has a good community, it's a good starting place for exploring rule engines, and will be the example used throughout this article. For more information on other rule engines, see the Other Rule Engines sidebar.

How do they work?

The underlying nature of the rule engine comes from the algorithm that drives it; some simple 'rule engines' simply chain procedural logic together in an order that you specify. Most offer sophisticated matching algorithms like Rete, Treat and Leaps to connect facts with rules, determine which rules should be run and in what order.

Drools, like many of its competitors, uses Rete, a matching algorithm developed by Charles Forgy. Although a detailed treatment of Rete is beyond the scope of this article, the simplified form is this: Rete builds a tree from the rules, like a state machine. Facts enter the tree at the top-level nodes as parameters to the rules, and work their way down the tree if they match the conditions until they reach the leaf nodes: rule consequences.

Rete's author and others have attempted to improve on the success of Rete with new algorithms, the results of which are Treat, Leaps, Rete II and Rete III, some of which are used by other rule engines.

These matching algorithms are more complex than simply running a series of code snippets in order. If you want to run every rule on every fact in order, a rule engine is likely to slow you down; there is overhead in the rule engine approach that you're not using. On the other hand, if the job of matching rules to data is non-trivial, the sophistication of a rule engine may well save time through mechanisms like condition sharing, wherein one condition shared in multiple rules can be evaluated once rather than once per rule.

For instance, if you're creating an educational scheduling system and wish to create and score every possible combination of students to classes to maximize for students getting the classes they desire, a rule engine may only slow you down in creating and scoring combinations. On the other hand, if you have an insurance system that must repeatedly query combinations of similar demographics, condition sharing and pattern matching may dramatically reduce the overall complexity of the solution without requiring any custom code to perform this orchestration.

How do you write rules?

This is an area in which rule engines vary widely. Some support one or more existing languages in relatively full or snippet form, while others offer a proprietary language, and others still offer a visual design system.

Some engines have extensive tool support for the creation and management of rules, including a rule development environment, while others leverage integrate with existing tools, ranging from IDEs to text editors and spreadsheets.

As of version 2.5, Drools offers support for coding rules in Java, Python and Groovy in an XML-based rule file or Java POJO rules wired up with Spring or JSE 5.0 annotations, as well as in custom-developed domain-specific languages.

Drools 3.0, due out shortly, has its own rule language that doesn't require an XML wrapper; since this language is more readable by new users, I'll use this syntax.

The previously-described car insurance rule, specified in Drools 3.0 DRL:

rule "High Risk"
Driver( male=true, age >= 16, age <= 25 )
Car ( style == Style.SPORT, color==Color.RED )
$p: Policy ()
$p.increasePremium( new Percentage( 20 ) );

Drools has very limited tooling of its own in Drools 2.5, but Drools 3.0 has an Eclipse plugin that supports the creation and debugging of rules, including domain-specific language extensions and auto-completion. This is likely the start of increasing tool support for Drools users.

What are they good for?

Rule engines are varied; each has a particular strength, and the benefits derived from using a rule engine are not always the same from engine to engine. However, there are some general qualities that apply to most rule engines.

First and foremost, rule engines are a different way to look at a problem - not necessarily better or worse, just different. Fundamentally, both general-purpose programming languages and rule engines can be used to solve a similar class of problems, although some problems are more easily solved with the use of a rule engine, and others with general-purpose programming languages.

Rule engines offer matching algorithms that can determine which rules need to be run and in which order, while still allowing the rule writers to exert some control over these factors when necessary with filters, workflow or conflict resolution. They allow facts to be reconsidered by rules as they change, allowing the consequence of one rule to affect the fact base, causing other rules to be run or cancelled as necessary.

These qualities create subtle interactions which can have powerful results, even when the facts and rules themselves are simple. Often, its these characteristics that offer value over a general purpose programming language.

Finally, as we discussed in the introduction, some people look to rules engines to fill a particular pattern: externalizing business logic, supporting rapid change or empowering business users. These approaches will be discussed in more detail below.

What aren't they good for?

Although rule engines can be applied to just about any problem if you try hard enough, they are simply another tool in the toolbox. They don't replace constraint programming and solvers, artificial intelligence, workflow engines, decision tables or general-purpose programming languages; they simply supplement that set of tools with another.

Example: Car Insurance Quotes

Although the application of rule engines is as varied as rule engines themselves, it's often useful to have a specific example in mind. Imagine a system for preparing car insurance quotes. It would require a series of screens to collect information about the customer, his or her driving record, current insurance, cars and information about the quote he or she would like to receive. Following this, the system would prepare an insurance quote and display it to the user, then seek acceptance, either then, or at a later date.

The bulk of that system is relatively amenable to development in general-purpose programming languages, but the process of preparing a quote from the customer's information is complicated, and deals with a lot of changing data, changing business rules, changing regulations, and is troublesome for a normal development process. In many ways, this is the sort of environment in which a rule engine can excel, and you might choose to delegate the quote preparation process to a rule engine.

Architecting with Rule Engines

The exact way in which a rule engine is integrated with the architecture of a new or existing system varies significantly with the specifics of a particular rule engine.

Servers and Embedded

Some rule engines desire or require their own server and can be invoked remotely; these are well-suited to environments already making use of distributed computing, such as enterprise javabeans, corba and service-oriented architecture. Others are fully embedded, working directly with your domain objects, modifying them directly. This approach is best employed when there is no trust barrier between the rules and the surrounding software, otherwise an anti-corruption layer may be necessary.

Rule Performance

Embedded rule engines are typically more performant for applications that are not already using distributed computing. Server-oriented rule engines can work well within a service-oriented environment and offer scalability within the rule engine itself.

Rule engines are a generalized way to select and orchestrate business rules, and are unlikely to ever be quite as rapid as a completely custom solution, but a well-architected rule solution should not be a significant performance loss for the overall application.

The performance of the rule solution is likely to fall on the shoulders of the structure and code of rules themselves, rather than the engine that fires them, but particular rule engines will have their own best practices with respect to performance. It's not uncommon for the overall pattern-matching algorithm to be run repeatedly while the rule engine is interpreting inputs and scheduling rules, so it's important to keep these dependencies as thin as possible. For instance, in Drools, conditions may be run repeatedly during the early scheduling process, so one or more particularly computationally-expensive and frequently-visited conditions can have a significant impact on the overall performance. Until you're familiar with developing rule solutions in a particular rule engine, it will be worth keeping an eye on performance as you develop.

Managing Rules

The rules themselves might be managed in a central repository and referred to through a unique identifier, or loaded directly from a datastore in the filesystem or in a database. Loading rules from the filesystem can make it easy to manage rules as part of an overall codebase in a source control system and make changes to rules without an extensive toolset. On the other hand, a rule management repository, alongside rule creation, management and deployment tools increase the ease with which non-developers can participate in the creation of rules without understanding source control systems and build scripts, or managing versions of files locally.

Drools is embedded, working directly with your domain objects. The rules can be loaded in a variety of ways, but a filesystem approach is the most common. It does not currently offer an out-of-the-box solution to loading from a database, or a central repository with management tools.

Example Architecture

Architecting a rule-engine solution to create quotes for Car Insurance might end up looking something like this:

By keeping the quotation rules outside of the web application archive, they can be deployed and redeployed on a separate lifecycle from the application that uses the server. Keeping the rules light and implementation agnostic, any information they need is passed into them, rather than being retrieved directly within the rules.

Rules are typically invoked by selecting a 'set' of rules to run, then asserting a series of facts into the rule engine. These facts are the objects to which rule parameters may bind and against which conditions and consequences may execute. Once one or more assertions have been made, the rule engine may be invoked. Although the 'set' of rules has been selected, the rule engine will choose which objects to run against which rules, and in what order. The rules may modify the existing set of objects, including removing some, and add new objects.

This approach, typical for rule engines, has some similarities to the Command design pattern, in that rule engines are typically used to execute one or more actions on the domain objects passed into the rule engine. Unlike the command pattern, rule engines are designed such that the objects on which the rules execute and the results of the rules aren't known by the underlying API. In order to use a particular set of rules, the invoker will need to understand the inputs and may need some understanding of the implied effects.

If the rule engine were Drools, then creation of a quotation might follow this sequence:

In this case, the rules (as represented by the loaded RuleBase object), are pulled into a stateful context, as represented by the WorkingMemory object. The current state is asserted into the working memory, the rules are fired, and any resulting quotations are returned. This same pattern could be used for a single quotation system (in the case of an insurance provider) or for a multi-quotation system (in the case of a broker).

By putting the rule engine behind a service façade for creating quotations, the implementation of employing a rule engine for this purpose is encapsulated. Existing investments in the user interface, services and data access do not require modifications. It might even allow easier entry into new markets by creating a separation of concerns between the data collection application and the market-sensitive rules, which could be updated, tested and re-deployed many times for a particular quotation service. A single quotation service could delegate to a one of many rule sets by region, underwriter, etc.

These simple examples of how a rule engine might be used in an insurance industry context are one choice among many; there are many other ways to integrate a rule engine into your application. Most importantly, these examples will serve as working examples for further discussion about the patterns and anti-patterns in the use of rule engines.

Rule Engine Patterns and Anti-Patterns

There are a number of ways in which people attempt to apply rule engines. Each of these ways has tradeoffs, and can inform your own use of rule engines in some manner. In most cases, the best way to explore these tradeoffs in your own environment is to work with a rule engine and experiment with the results.

Externalize Business Logic

Many businesses choose a rule engine as a way to externalize business rules from the application itself. This allows rules to be developed, possibly tested, and deployed on a different lifecycle from the application itself. This is often in support of a deeper business need such as support for rapid change, addressed next. It can allow the application's key business rules to be changed without recompiling and redeploying the application and can establish a separation of concerns.

In our insurance example, it can allow the largely static features of data collection, quote presentation to be developed on one lifecycle while the specific rules that drive the creation of an insurance quote, driven by regulations, the market and underwriting statistics, could be developed on an entirely different lifecycle.

Rule engines are a viable way to externalize business rules, and can have the benefits these teams are seeking. That said, it's true that these same benefits can be achieved in other ways, so it's important to weigh the benefits of this approach when compared with the alternatives.

If your only concern is to separate the deployment of business rules from the application, using a rule engine is probably not the easiest way to accomplish this. Concrete implementations of business rules can be loaded and reloaded at runtime. If your concern is to avoid an explicit recompilation cycle, consider using scripting languages or dynamic recompilation technologies like Janino.

Many of these options require less investment of time, energy and licensing costs when compared to rule engines, although rule engines to have a lot to offer in other areas.

Rapidity of Change

In some environments, business rules change frequently, even constantly. Some teams seek to use rule engines to support these kinds of change.

For instance, by externalizing the business rules from the application code, you may enable these rules to be changed and deployed separately, even hot-deployed, as described in the previous section. In addition, some rule engines support visual design or dynamic languages, which can support a rapid development approach.

Because insurance regulations and new statistics can result in changes to quotes that must be applied in short timeframes, it can be important to know that the logic that drives the creation of rules can support these kinds of changes quickly.

Ultimately, rule engines are not a magic bullet for supporting change, but they can help. Although many of these same approaches can be taken with or without a rule engine, implementing these approaches by hand may not be as cost-effective as making use of the tools that come with a particular rule engine.

As in the externalization of business logic, if rapidity of change is your only concern, then you might want to consider rapid development tools and/or scripting languages, which might require less investment of effort, time and money than rule engines.

Regardless of the technology used, it's important to stress that even (and especially) in an environment of rapid change, testing is a paramount concern. Changing complex business rules in a running application without thorough testing is a recipe for disaster.

Business Users Develop Business Rules

Business rules are usually defined by business personnel, but in most businesses, they are implemented by developers. Some teams turn to rule engines to empower their business users to develop the rules with no (or, at least, reduced) developer support. When successful, this encourages business users to take control over the business solution, stay involved and understand what is or is not possible, turning to the developers only when they need radically new capabilities.

It's often true that rule engines are an improvement in this regard, but it's also true that most rule solutions of even moderate complexity will require some involvement by developers. Even if the tool support is extremely easy-to-use, there's a technical analysis mindset and rigor that many business users have not needed to develop when considering edge cases and extreme conditions.

The most successful solutions in this space empower a sort of collaboration between developers and business users where developers create the boundaries, set up the grand structures, and deal with any necessary technical complexities while allowing business users to customize the specifics of individual business rules within a more controlled environment.

For an automotive insurance broker, the ability to employ developers to create sophisticated decision tables in advance that can be configured-for particular markets, or in support of changes-with varying economic parameters by business users can be a powerful approach.

In some of our more recent projects, we've been able to use Drools' "decision tables" to great effect. Java developers create and enhance the business domain and the application that delegates business decisions to the rules, and have created a series of decision table structures in multiple Excel workbooks that form the boundaries of the solution. Team members from the analytical side of the business are then able to create rules by parameterizing these tables to accomplish business goals, like this:

RuleTable agerRules (Policy policy, Driver driver)
driver.getAge() >$param driver.getAge() <=$param policy.setPremium( $param)
Age Greater Than Age Less Than or Equal Premium
16 25 150%
25 40 100%
40 60 80%
60 200 125%

This has been fairly successful, and after several iterations of this work, the business users are increasingly seeking to take on more responsibilities in creating the decision table structures with developer support.

When looking for a rule engine to fill this rule, it's important to identify the business users who you expect to create or modify business rules and have them examine and, preferably, use the tools to develop some realistic scenarios, rather than simply accept marketing claims about suitability for business users. Alternately, consider a product with a suitably long evaluation period that would allow some exploration before committing to tools that your business users discover they don't want to use.

Relinquish Flow Control

Allowing the rule engine to determine which rules to run and when is an important step in making use of the full power of a rule engine. Developers who are used to having control over the execution of their business logic may find this transition troublesome, but once you've grown accustomed to this way of working, you're likely to find that it offers a fair amount of power.

Many rule engines allow the process of selecting and ordering rules to be influenced. Drools offers an array of conflict resolution strategies and rule attributes such as salience and agenda groups to control them. These kinds of options allow you to leave overall flow control to the rules but exert whatever control is necessary to fulfill your business logic or to tune approach the rule engine adopts.

Rules as Procedural Code

If you've done a fair amount of procedural programming, switching over to a rule-oriented model of development can be a little disorienting. You're likely to slip into familiar development patterns for procedural development and ask questions like:

  • How do I run only one rule?
  • Can I list the rules I want to run, in order?

It's important to realize that it's possible to do these things, but it's not always advisable. For instance, in Drools, you can control which rules are run by using activation filters, and you can control the order using conflict resolution. Resist these options: sometimes they are necessary, but it's easy to reach for these controls too soon as a knee-jerk reaction to old procedural paradigms.

The Cross Product

When assembling facts to supply to a rule engine, it's easy to forget the combinatorics involved and that when small numbers of individual facts are combined with small number of rules, the resulting number of combinations can get surprisingly large quickly.

If an insurance rule looks at two cars and one insured customer and the knowledgebase consists of five thousand cars and five thousand owners, the engine may need to consider twenty-five million combinations of cars for each of five thousand owners, or one hundred twenty-five billion total combinations.

If you fail to consider the scale of what you're asking the rule engine to compare, then you might write rules like these and discover that the performance of your solution is not what you had expected.

Accordingly, when developing a rule solution, it's important to keep a sense of scale; this can be done by limiting the amount of information that you pass in and out of any given set of rules, or through regular performance and load testing to establish and evaluate metrics on an ongoing basis.

Combinations and Permutations

If one rule takes more than one fact of a particular type, rule engines may supply all permutations and combinations of those facts. So, if the knowledge base has three drivers (Adam, Beth, Chris) and a rule requires two of these, the rule engine might execute that rule nine times with the following combinations of people: (Adam, Adam), (Adam, Beth), (Adam, Chris), (Beth, Adam), (Beth, Beth), (Beth, Chris), (Chris, Adam), (Chris, Beth), (Chris, Chris).

This can be desired behavior for some facts, but in other case, you may want to be supplied with unique combinations, or with combinations that have no duplicates. This is usually performed with conditions on the rules.

To avoid duplicate items in your rule, simply add a condition that the first item not be the same as the second, removing the following tuples: (Adam, Adam), (Beth, Beth), (Chris, Chris) and leaving these six: (Adam, Beth), (Adam, Chris), (Beth, Adam), (Beth, Chris), (Chris, Adam), (Chris, Beth).

In order to get unique combinations, you can use object ordering, such as requiring the second person's name to be alphabetically after the first person's, removing the following tuples: (Beth, Adam), (Chris, Adam), (Chris, Beth) and leaving these three: (Adam, Beth), (Adam, Chris), (Beth, Chris). This can even be performed using the object's hash code in Java.

Some rule engines have features to reduce these combinations and permutations for you, so it's important to understand the features of your rule engine and how it supplies these combinations. Drools 2 provided all of these combinations. Drools 3-at least in its RC2 incarnation-removes the duplicate items with an identity filter.


As in any form of flow control with software, self-recursion can be an issue. For instance, if a rule modifies the fact-base, will that change cause the rule to be run again, resulting in an infinite loop?

Alternately, you might create a cycle of rules where one triggers another which eventually ends up triggering the first again. These kinds of cycles are possible if you don't prevent them in the design of your rules.

There are solutions - in some cases, as simple as noting, when defining a rule, that it should not trigger itself. Drools has a 'no loop' attribute for this case. In many cases, the best solution is to ensure that the rules are designed in such a way as to not trigger themselves.

For instance, if you were using a rule to apply a discount to an insurance quotation, you might simply make one of the conditions of the rule that the quote not have a discount specified. Not only does this mean that the discount rule won't trigger itself, but it allows for the possibility that you could have a hierarchy of discount rules, with the general case having the lowest priority and they would interact cleanly.

These kinds of design decisions become natural when you've spent enough time developing rules for a rule engine, but are not immediately obvious if your experience has been in procedural or object-oriented code.


When using rule engines for the first time, it's tempting to use the granularity of rules and the data that drives them to return to typical programming paradigms, such as:

  • Writing large and complicated rules that move flow control within the rule rather than the rule engine.
  • Supplying large object graphs to the rule engine, navigating the object graph in the rule itself rather than using fine-grained facts and allowing the rule engine to select those facts that are appropriate.

These approaches allow you to integrate a rule engine into existing applications more rapidly and control your exposure to risk from adopting new technology. However, they also limit the power of your rule solution. Giving the rule engine greater freedom is the best way to get increased value from the implementation. In order to relax your control over the rule engine:

  • Create small, fine-grained, simple rules.
  • Supply the rules with small, fine-grained, simple facts.

These rules are more easily enabled and disabled; can be combined and recombined easily; can be re-ordered through conflict resolution in the rule engine and are easy to comprehend and write, even for non-developers. However, when they are applied in concert as a rule set, the results can be surprisingly complex.

Using Drools' Java/XML rules as a basis, consider the following example:

rule "Age Rule"
$p: Policy ( declined == false )
Driver driver = $p.getPrimaryDriver();
if( driver.getAge() < 25 || driver.getAge() > 60 )
$p.increasePremium( new Percentage( 50 ) );
else if( driver.getAge() <= 60 && driver.getAge() < 40 )
$p.discountPremium( new Percentage( 20 ) );

In this case, the consequence contains conditional logic, which is usually a sign that more than one rule is preferable. Similarly, by relying on the domain object navigation from policy to driver, it makes it difficult to write rules that need a driver but not a policy.

This rule could be re-written as several rules in this form:

rule "Young Premium"
$d: Driver ( age < 25 )
$p: Policy ( declined == false, $pd:primaryDriver -> ($pd==$d) )
$p.increasePremium( new Percentage( 50 ) );

These rules are simpler, easier to write and understand, and use facts that are treated in a less interconnected manner.

In Summary

Rule engines are a useful tool that can be used to externalize business logic, allow business users to write rules, or solve certain classes of problems in an efficient way. When compared to other approaches, there are tradeoffs, so it's important to approach this decision in a methodical manner:

  • Understand what problems you're trying to solve, what you're trying to accomplish
  • Investigate if rule engines in general or a specific rule engine will help you achieve those goals

In general, you might consider a business rule solution if you need to externalize business rules, support rapid change and empower business users to change business rules. You'll get the most out of a rule engine if you accept the new paradigm by relinquishing flow control, using fine-grained rules and objects, avoiding cross-products, and understanding the combinatorics and recursion that a rule approach can create.

If, after consideration, you're not sure that a rule engine will accomplish what you need, consider developing a low-cost proof-of-concept with a freely available rule engine like Drools and evaluate for yourself if a rule engine is right for your problem space.

If you decide to adopt a rule engine as part of your solution, accept that rule engines require some new skills in design and development, and expect some increased development costs and missteps until your team has acquired these skills.

Sidebar: Other Rule Engines

Although we've used Drools as our example rule engine for this article, there are others, both open-source and commercial ones. An exhaustive list of these and how they compare is well beyond the scope of this article, but here are some pointers to some of the more common alternatives:

Many of these rule engines, particularly the commercial ones, offer many more features, services, modules, components, and tools than we've been able to describe in detail here. When selecting a rule engine you may want to consider some of the following:

  • Tools to empower both business users and developers, and the collaboration between the two
  • Tools for rule authoring: web-based, desktop-based, integrated with developer and business users tools such as Eclipse or Excel
  • Tools for rule testing: test, simulation, error-checking and debugging, dependency checks
  • Rule algorithm and performance
  • Workflows for rule editing, review and approval Rule management repositories with versioning
  • Integration with existing software and build architecture
  • Operational support: monitoring, auditing
  • Training and Support

About the author

Geoffrey Wiseman is a software development generalist, writer occasional consultant and all-around enthusiast. In recent years, he has been working on a customer value managment product for Exchange Solutions with an emphasis on the financial services industry. Between development for fun and profit, reading and writing software specs, bouts of flickr, playing video games, reading and writing software specs, contributing where he can to open source communities, attempting to read all the interesting software content on the entire internet, and generally enjoying himself, he found some time to write this article.

Rate this Article