Web List Functionality

By Krishna, June 22, 2008

Initial implementations of lists in a web application are generally very simple. As the list grows in size, more functionality is required. Here is an attempt at compiling the major functional requirements of a list. You may not use many of these functions, but it is useful to know what is possible.

Searching

image

There should be an easy way for users to search all fields of the entire list. The simplest implementation would be a single textbox with a Search button. Users may then want to search on specific fields. A straightforward method is to present separate input fields for each searchable field. This however limits the user to searching each field for one particular value. You may have to present additional fields for specifying ranges or conditional operators (like greater than or equal to).

image

For more complex conditions, it may be helpful to present a query language like a simplified version of SQL. Or better still, provide a query builder, where the user can create a complex query and execute it. This is more preferable as it reduces the learning curve from beginner to intermediate. The query builder should also provide for searching dynamic values such as “today”, “current time”, etc. and aggregate functions such as “total sales is greater than x”.

Users may wish to save their queries for a later time. It should be easy to give them a meaningful name, description and even allow for tagging. Sometimes, the user may want to save the results of the query (a snapshot in time) instead of the query itself. Saving a data set may only be possible if the list size is small. Otherwise it should allow the users to export the information out to a document in Word, PDF, Excel or other formats.

Paging

Paging is more a practical than usability concern. Users may wish to have all records displayed to them, but doing so would be time-prohibitive and involve unnecessary data processing, when the user is interested in just a few records. Paging is generally implemented by links to different pages, though nowadays, it is common to see the “infinite” page where vertically scrolling down displays more records fetched dynamically that weren’t there initially. Live Image Search and Google Reader (see below) have good implementations of the latter design.

image image

If the list is organized alphabetically or chronologically, being able to access a specific page is a more useful feature than displaying an infinite page. The reverse is true if the list organizes the results by relevance or randomly. For example, you might guess that the middle page of a contact list may contain names starting with “K” or “M”, but retrieving the 100th page of Yahoo! search result page is meaningless. For the former case, provide links or “jump-to-page” functionality at the top and bottom of the list.

Users would also find it useful to control the number of results per page. A global setting is usually appropriate, though it is more helpful to provide a setting in each list screen and remember the last selected value. For the default page size, choose a value that returns a reasonable-sized amount of data without the user having to navigate or scroll too much. Somewhere between 10 and 25 seems like a good guess, but you can make a more educated decision by observing your users and finding out how much they click to a different page or scroll for each page.

Displaying the number of results across all pages can be useful. But performance concerns may prevent you from giving an accurate value. In such cases, you can do what Gmail does, give an estimate:

image

Sorting

The most common implementation of sorting results is doubling the header column as a sort column. You click on a header column; the data sorts by that field. You click again; it sorts in the reverse order. Display the sort direction as an icon or arrow in the column. The limitation is that you can generally only sort the data by one column. Also, the sorting is not linked to the search functionality, so each time the user issues a search, he or she has to sort the search results again.

image

A better implementation would be to allow the user to specify multiple sorting criteria, so that the user can say, sort the contacts by city and then by last name within each city. This can be part of the search section and so each saved search can have its own sort. You can still retain the column heading sort, but it would be overridden by the search sort condition when the search executes. But after the results are displayed, clicking on the columns would override the search sorting, thus avoiding the need for users to change the search sort condition for a temporary sorting need.

There are client-side implementations of sorting data, but they should be avoided because generally the client does not receive all the data because of paging. Sorting the single page of results provides incorrect results from the right method of first sorting the entire data and displaying the first page. Another mistake commonly seen in client-side sorting is that numeric values or dates are sorted as if they were strings.

If the user has not sorted the data, it should default to the most meaningful sort. For example, contacts should be sorted alphabetically. Bills should be sorted by the one pending for the longest time.

Layout and Information

Should you have lines for separating the columns? What should be the font size of the columns? Should you display alternating rows in different colors? There are so many different choices that you can make for presenting the list information. Let me point you to Matt Berseth for some examples (and code!). Of course, tables do not have to be your first choice, either. You could present the data in a visual manner, such as graphs or maps. In such visual displays, paging and sorting are less of a concern, but searching (or filtering) remains important.

While designing the layout, you should be concerned about noise. The user is generally interested in only a few records and perhaps only a few columns. Searching solves the first problem. Providing the ability to show and hide columns helps with the second. I think it is better to allow selection of necessary columns when providing search criteria, and then allow a separate override when the search results are displayed.

Summary headers or footers are useful. While the conventional method is to have a summary row, on the web, providing the summary information in the header can avoid scrolling. You can even create a summary that provides information other than an aggregate function of a particular column. Of course, it goes without saying that the summary information must be emphasized using a greater font size or color.

List Management

Users want to do more with their lists. People like list management because it reminds them of a spreadsheet program like Microsoft Excel, which makes it very easy to add and change data. Though a web application has other better features, users want it all. A common need is to apply an action across multiple items in the list, such as Delete or Mark as Read. We see this uniformly implemented using a checkbox column. Users can choose the required items and select an action. While this works effectively, it limits the user to working with the current page of results.

Many implementations also have a master checkbox (in the header column) that selects or unselects all the checkboxes in the list. However, Gmail differs by offering text links for selecting “All”, “None”, “Starred”, etc. Hotmail has, perhaps, the worst implementation of the checkbox column. First, the checkbox is hidden behind an envelope icon. Second, if you miss the checkbox when clicking, it opens the message, making you lose all the previous checkbox selections you made.

image

With the increase in Ajax-enabled applications, users are demanding more from the list screen, such as the ability to add and edit records right in the grid. This works well when the number of fields in a particular record is relatively few. When a record has more fields, the user is forced to scroll horizontally. Some fields may require more advanced controls making the page code-heavy and unwieldy.

Security

A final piece of the puzzle is security. Some records must be hidden or disabled for some actions for different groups of users. A simple case would be each user owning the records that they have created and others unable to view them, unless the user decides to make them public or share them with select friends. This is a common situation with most social networks.

Advanced security may require an authorization module that allows the system to define user roles and access rights. Building this or re-using an existing framework may take a life of its own especially if security is very fine-grained, because the authorization module is linked to every part of the application. Keeping it simpler can mean easier development.

Security is a large and complex domain. This is one area that you will have to pay increasing attention to as your application gains more users. Managing the authorization framework, managing the needs of the end users and continuing to build your application can be quite demanding. So plan well for it.

Why Leadership?

By Krishna, June 17, 2008

The powers-that-be of the cricketing world have finally come to their senses and decided to allow players to challenge an umpire’s decision. The umpire can now use slow motion replays, microphones and other technology to make a more accurate decision. For those unfamiliar with cricket, this is the culmination of events that were set forth after a match between India and Australia marred by some egregious umpiring decisions threatened to snowball into a diplomatic crisis.

For the spectator, a decision like this is a no-brainer. You don’t want your team to lose because of an incorrect decision, nor would you be thrilled when they win only because the umpire helped them. Yet, the decision makers kept finding all kinds of reasons to avoid using technology: “Officials must be respected!“, “Technology is not infallible!“, “Bad decisions are part of the game!“, and so on

In the general context of organizational behavior, this is not very surprising. Organizations tend to do nothing until their hand is forced by a crisis. But this kind of behavior does reveal some patterns why organization behave so. Small mistakes are easily forgotten, especially when they make little difference to the final outcome (for example, most games are not affected by bad decisions by officials). People also like to move on, instead of dwelling on mistakes. People who are negatively affected have little decision making power. This is true for every consumer, and in many cases, a monopoly prevents the consumer from taking his/her business elsewhere.

It is useful to pay attention to the small mistakes to prevent them from causing harm in the future. But that option can also be taken too far, where every mistake is caught and people taken to task. Such an environment breeds fear and prevents people from taking any risks. Another problem is that you may over-react to infrequent problems and cause more harm by trying to fix something best left alone.

The situation explains why leadership is so important. Without leadership, it is very easy to ignore problems or create new ones by being panicky. Leadership provides the right organizational setup, where people can take risks while continually evaluating their processes and decisions without creating a climate of fear and mistrust. Crisis prevention, not crisis management should be the guiding principle of good leadership.

Actionable Web Bookmarks

By Krishna, June 16, 2008

I have been using web bookmarks using various applications like del.icio.us, Yahoo Bookmarks and Google Bookmarks, as well as the built-in functionality within the browsers. Although all of them have good features, one important thing that is missing from them is the ability to tie actions to them. Here is what I would like to ideally do with different kind of bookmarks. Some applications have partial functionality available and I have enclosed them in parentheses.

  1. Mark a bookmark as something I will use on a daily basis, such as login pages for various accounts. I should have an option to open all such bookmarks in different tabs in the browser (Firefox/IE does this).
  2. Mark as something that I may need infrequently. I might have different levels of frequency of use. So I would want to classify them as such.
  3. Mark a bookmark to visit on a particular date, or on a recurring basis. Example: a bill payer or bank site. I should get a reminder in email to visit the site.
  4. Mark as read and archive with an option to share. This might be an article that I want to read, but I have no use after reading it. I might want to email or share the bookmark with others in my network (del.icio.us and Google Reader does this).
  5. Mark a bookmark as the definitive answer to a problem I faced. The application should keep track of why I bookmarked the answer, so that it can help me answer the question in the future (Google Web History remembers the search and the associated search result I clicked on).
  6. I would like to know if someone visited the bookmark I shared with them.

Most bookmarking sites are concerned with categorizing bookmarks based on content, or sharing the bookmarks with others in the social network. That is helpful, but the bigger benefit for an end user is answering the questions: What does the end user want to do with the bookmark? When do they want to perform an action based on the bookmark?

Pair Programming and Code Reviews

By Krishna, June 15, 2008

One simple definition of pair programming would be continuous code review. Instead of doing code reviews at definite intervals, one programmer continuously monitors the code written by his/her pair programmer. One could have yet other code reviewers in addition to the pair programmers, but in general, the principle stands.

A single programmer producing code without any code reviews may be the most efficient scenario, if it didn’t conflict with the reality that programmers make mistakes. Those mistakes are uncovered during testing and the programmer is forced to spend additional time fixing those mistakes. The effort and time involved in fixing a bug is greater than that involved in correcting it before the code was sent for testing.

One reason is the time taken to perform the release processes again after the bug is fixed. Second, the process of fixing the bug involves the programmer to spend time re-understanding the code and debugging. Thus, combining programming with code reviews increases the efficiency of the programmer.

Pair programming, considering that it is constant code review, should increase the correctness of work produced by a programmer. At the same time, it keeps one programmer off producing code. So does the efficiency of producing correct code balance the inefficiency of keeping one person idle? There are studies done to find how efficient pair programming is, but the jury is still out. However, I think a few ideas may be useful in deciding what works in your situation.

First of all, there are many development activities that lend themselves well to working in pairs or small teams. Design, at a higher architectural level or lower algorithmic level, is one such activity. Working together can help team members complement each other, providing ideas and correcting errors as they occur. I have often found that working on a relational database design in a team can be very productive.

On the other hand, some programming activities can be done in isolation. For example, if you have a well-defined HTML prototype, re-creating that in a web programming language (by a reasonably competent developer) does not require much oversight. Or, if your application requires a lot of boilerplate code and you are not using code generators, you could have developers work on it independently.

Another concern is that pair programming can avoid distractions and help you make the most use of the available time. This is one reason why study groups are so popular – each individual can maximize the time spent in productive activity. Assuming that pair programming makes more time available for development, it may be more efficient than a simple comparison with the work done by a single programmer alone would imply.

Project Scope and Bug Density

By Krishna, June 9, 2008

One important consequence of bug reports is that they not only affect your schedule, but also conflict with your priorities. Release of product features is negotiable to some extent. A critical bug seldom is. You have to postpone much of your planned work until the bug fix is released. That step requires you to perform the release management steps again, so your quality staff members are also involved and they cannot pursue other planned activities.

The other important problem with bugs is that every bug lowers the confidence of the stakeholders in both the product and the creators of the product. The bug does not have to be serious to reduce confidence. It can be a minor bug that is irritating or confusing or careless. Lack of confidence reduces the effort that users are willing to put in to learn the system.

There are many ways to improve quality in your project and reduce the bug count such as different forms of testing, code reviews, etc. But there is a certain limit to quality that you can achieve with the normal constraints of a project (available time and budget, quality of technical resources, etc.) Your limit may be higher or lower than other projects, but it exists, and you cannot improve the level of quality by performing more quality activities because the law of diminishing returns kicks in.

Assuming that you are operating at your highest quality level, the quality of your product is proportional to the size of your product. The more features and code it has, the greater number of potential bugs, assuming that bug-to-feature ratio stays constant. That is why the suggestion by 37signals to build less seems very attractive. 37signals has a more general (and sometimes controversial) philosophy than just reducing bugs, but let us focus on bugs for now.

It does not seem practical at first glance to reduce product size. After all, customers demand features. More functionality sells more products. And there is nothing more capable than a rush of customers leaving you for other products to increase product scope dramatically. But if we cannot reduce product features, what can we do?

One answer is to tightly integrate features instead of distributing them throughout the application. For example, instead of building different screens for displaying records for data entry purposes and for reporting, merge the two screens. Provide more information on a single screen instead of breaking it up into multiple tabs. Avoid wizard screens and make all your screens easily learnable for the beginner user.

So reduce the project size to project feature ratio. This requires more work because you have to create a more visually compelling and intuitive user design. And your code will have to be modified to make it more streamlined and able to implement more complex needs. But in the end, having an overall smaller code base and project size earns its reward in higher quality, greater consumer confidence and better project planning.

Teams and Disagreements

By Krishna, June 8, 2008

The common image that comes to mind when we talk about “teams” is a group of people that work jointly to achieve a particular objective. We think of charismatic leaders who take the team from vision to goal. Disagreements and arguments are often thought of in the context of dysfunctional teams, those that fail despite the talents of the individuals involved in the team.

But even the most successful teams are composed of individuals, often with widely varying backgrounds, ideas and opinions. Is the successful team therefore built on compromise? Do successful teams have no arguments within? Do team members relinquish their opinions and simply trust in the leader?

Let us try to classify teams on the basis of internal disagreements and the basis for the disagreements.

Team members
tend to agree
Team members
tend to disagree
Team members driven by
self-interest
A B
Team members driven by
team goal
C D

In Group A, team members, driven by self-interest, never disagree. Why would someone agree publicly to something that they privately disagree with? This would only happen if they would encounter harm by voicing their disagreement. An example would be a dictatorial leader, who treats disagreement as lack of loyalty, respect or patriotism. Or a group-think situation that anyone who presents an opposing thought is viewed as foolish, naive or ignorant. The physical and emotional risks involved in making an unpopular stand may make it wiser for an individual to keep silent.

Being different does not mean being correct. The majority opinion or the leader’s choice may be the right one. But no one is infallible. And so, in the cases where it is wrong, there is no alternative choice to fall back to. Too often, people do not even accept that failure has occurred and suppress those who report the bad news. Such a team structure is a disaster waiting to happen.

In Group B, there is much disagreement, but everyone is acting out of self-interest. This most commonly occurs in a divided legislative body (such as Congress) in the absence of strong and bipartisan leaders. Every politician is answerable to his or her constituency – they are sent to Congress or Parliament to argue for their voters. At the same time, they also have an obligation towards the nation. In the absence of a strong national consensus on a subject, elected politicians favor the position that would get them elected again, not one which benefits more people. We also see the same dynamic in bureaucracies where different groups argue for the position that would be most favorable to their department.

This situation does provide benefit to some people, and even people who do not benefit understand how the game is played and behave accordingly. In the end, almost everyone loses because no one failed to provide for the common benefit. Vast amounts of time and money would be spent before anyone recognizes the harm done.

In Group C, team members work towards a common goal and rarely voice any disagreement. Even though it is not harmful to them to voice their disagreement, they still agree with the leader and the group. The only possibility is that they believe that any contradicting thoughts they have are wrong or misguided. Instead they believe in the rightfulness of the leader or the group. For example, consider the CEO of a Fortune 500 company with a 30-year successful track record in a group with new recruits fresh out of college. Every word uttered by the executive would be taken as gospel by the group, even in areas that he or she may have little experience in or knowledge of.

When the group places too much belief in the leader, they can miss even obvious mistakes in decision making. Leaders have to actively step back and let subordinates take greater responsibility to avoid this trap. The problem with leading too much is that when the leader is gone or missing, the group ceases to function or make any decisions.

In Group D, the members of the group voice their disagreements openly, but they do not think of themselves. They want the group to succeed and they have the same goals. They disagree about strategies and tactics. So what happens? If they never reach agreement, they cannot function and hence may have to split into different groups, each using different methods to achieve the same goals. For example, in parliamentary systems, there will be different parties with similar stated objectives, but radically different economic policies to achieve them.

But a team comprising people with different opinions could also stay and work together. This situation could easily devolve into a Group B situation, where the reason for staying together is to win and share the spoils. But if that does not happen, how could a team survive and stay together even though there are internal disagreements?

One way for people to stay together is the knowledge that everyone is acting not selfishly, but for the common goal. That knowledge comes from common belief systems or from sacrifices made by the team members for one another or for the team. Teams bond when they go through emotional experiences together. However, the more diverse the members of group, the greater work that must be done by the leader to forge common bonds.

Secondly, having disagreements does not meet lack of decision making. Decisions should be made for pursuing a course of action (or inaction, as the case may be). But all decision-making should follow these important rules:

  1. Everyone agrees that each person in the team owns the decision. Hence, there will be no “I told you so” if a decision fails. If this is not done, the dissenters will wait for failure while the supporters will hide or spin any mistakes. With this rule in place, everyone works to make the decision a success and step in when a fellow team member needs help.
  2. Everyone agrees that the decision is the best that could be made in the present circumstances and considering all options. That means that future circumstances could possibly make the decision seems wrong with the benefit of hindsight. This rule helps avoid finger-pointing and wishful thinking when things don’t go as expected.
  3. Everyone agrees that the decision will be final, ending further discussions until new circumstances come about or new facts are uncovered. This avoids private discussions that may result in lower morale and possibly sabotage.

Decision making can be made in many different ways, but any process should not rely on a method that fails to bring all opinions to light. For example, using a simple majority to make a decision may be counter-productive even though everyone had a voice (“vote”), if the decision makers have not taken the time to understand the consequences of the decision. And decision-making does not necessarily mean a democratic process. It could be a manager who has extensive discussions with his team before accepting a minority opinion that has a greater resonance with facts.

People often confuse a successful team with a team that has achieved success. A successful team transcends the divisions and differences of its constituents. The results of its efforts are dependent on many factors, and so a successful team may not always meet its goals. But its ability to tolerate different views and adapt to changing circumstances provides it with a greater potential to achieve success in the long run.

Vocabulary

By Krishna, June 2, 2008

I was chatting with my friend the other day and we got talking about vocabulary. In his case, he was interested in some short-term solutions to the problem because of an approaching exam. I didn’t have many ideas at that time, but later, I came up with some thoughts:

  • Read political writing in blogs and newspaper editorials. The writing is generally done by trained journalists who are very good writers. Politics also covers a wide variety of topics (economics, law, etc.) and you will be exposed to many different words in short time. Any of the major newspapers (liberal or conservative) or magazines would do the trick as long as you consistently read them. Blogs are even more convenient. Reading a few articles daily takes just a few minutes, but pays a high dividend.
  • An easy book to learn big words is Norman Lewis’s “Word Power Made Easy“. It contains several exercises and mnemonics. I suppose there are many similar books in the same category to improve vocabulary, but this is the only one I have read. My recommendation is to get at least one book in the category and work through it.

Short-term vocabulary improvement requires making a conscious, active effort at learning. This can mean writing down new words that you are unfamiliar with and looking them up in a dictionary. You should try to use these new words in your writing or speech until you become comfortable with them. Sometimes, a poor choice of words may result in embarrassment, but such problems come with the territory when you are in learning mode.

A long-term commitment to improving vocabulary can be passive. The easiest way is to read whenever you can and your subconscious mind will automatically learn new words. Fictional works will introduce you to generic, but obscure words, while non-fiction will give you jargon, terminology specific to the area you are reading. The trick is repetition. The more often you see a word, the more your brain understands the context and is able to notice it then and recall it at a later time.

Writing is a good way to strengthen your knowledge of words. You don’t have to write articles or blogs to do this. It could be as simple as trying to write better emails to people, or writing full sentences when you exchange instant messages with someone. You could use the opportunities when you are asked to prepare any written material (say technical documentation) to use the words that you come across.

Listening to famous speeches and podcasts is another way to improve recognition of words, the only problem being that it is sometimes inconvenient to interrupt your listening and record the new words that you are hearing. Joining a Toastmasters Club or a local debating association will also help you improve vocabulary skills (and of course, your speech-making capability). Public speaking forces you to be structured and accurate with your words. In addition, you also get to hear talented speakers.

To Learn C or Not

By Krishna, June 1, 2008

The past few weeks, I have been listening to the podcasts of Joel Spolsky and Jeff Atwood at stackoverflow.com. The discussions are perhaps not as informative as a Hanselminutes podcast, but they are definitely entertaining and cover many topics related to software development. Atwood has posted some of the discussed topics on Coding Horror. One of the subjects that caught my attention (and others as well) was the fact that Jeff did not know the C language and didn’t feel that it was necessary, while Joel holds the opposite view.

This debate is different from most language fights. Most of them are about which programming language you must use. Here, no one is suggesting that you use C to replace your current programming language. The C advocates recommend that you learn C to gain a more fundamental idea of the low-level issues of programming, something that current languages isolate developers from. Since my first programming language was C, my first instinct was to agree with Joel, and then I realized several problems in my reasoning.

The first mistake was to forget the important place that C had in my learning curve. When I moved to C++, I was only learning the object-oriented features provided on top of C. The basic language structure (except for the OO constructs) remained the same. Moving to Java from C++ once again required only learning a few additional features and unlearning some of the C++ features. Thus, with every other language (C#, VB, Python, etc.), the learning process was always about trying to relate my existing knowledge to the feature set in the new language.

For a person who started with C, it is difficult to imagine how someone else could have learnt anything without the initial C knowledge. But if that person had started with Visual Basic, they would have learnt many fundamental aspects of programming (such as logical structure of programs, debugging, etc.) through those languages. And that person, when moving to other languages, would use their VB knowledge to understand and learn them.

The difference here is like mother tongues. For example, one person is a native English speaker and another is a French or German citizen who learnt English as a second language. But regardless, both can understand and speak English fluently, despite the fact that one of them “thinks” in another language.

But what about the advanced stuff? What about pointers and dynamic memory allocation? I think a fallacy here is to compare an advanced C programmer and a naive VB or Java programmer. If we look at the skills of an advanced programmer in any language, they would have the same level of understanding complex concepts, algorithms and data structures. The point being, you have to look at more than the programming language knowledge of a developer to understand his or her true worth.

I think it is instructive to look at the reasons why C was displaced in the first place by other languages. C has many peculiarities that get in the way of writing business logic. For example, different sizes of data types on different operating systems, null-terminated strings, etc. While knowing these intricacies gives you a better idea of what goes under the hood, it does not help you solve your business problems any faster. Secondly, they can even hinder learning. For example, a language that has automatic garbage collection may make it easier to learn data structures or algorithms without the C-specific pointer and memory handling.

A C programmer may have to unlearn some lessons when they move on to higher-level languages. The advanced C programmer has a tendency to write tight, clever code, sometimes at the cost of making it incomprehensible for anyone else. The lesser C programmers ignore concepts of data encapsulation and tend to create classes that are simply a bunch of related functions that keep shuffling data around.

It is also a mistake to assume that just because a programming student has learnt C, that they have somehow achieved a state of greater understanding. It is possible to write non-trivial large programs in C while staying away from complex constructs (in fact, it may make for less debugging even if it introduces inefficiency). A bad programmer can also make bad mistakes with pointers and memory allocation, and have a program that runs correctly for many different test cases and for long periods, just because it didn’t hit the error conditions.

The final fallacy is a generational problem. The older generation always feels that the younger generation have had it easy so far, and because of their inexperience with failure, they are on the verge of making some colossal errors. The idea goes like this, “We suffered. We learnt the hard way. And that is why we are successful today.” Hence, there must be something wrong with the success of the new generation. Maybe they are just lucky. This argument, of course, is not unique to programming.

But perhaps, it is possible that maybe you don’t have to fail before you succeed. Maybe you don’t need to go through all the hardships and sacrifices suffered by the people before you. The new generation can start programming in their simplistic programming language and still create works of wonder without ever knowing one hardware term. Maybe the rules have changed.

At some level, I think people understand this. But there is a real sense of nostalgia that believes that the new generation will never get to experience what they had. The feelings of achievement on solving a complex problem. The respect and love for the programming profession. The constant quest for learning. The acquisition of minor trivia. The knowledge that makes inside jokes funny. But this sense of loss ignores the fact that different people can reach the same goals by different journeys.

A real programmer is always on the lookout for improvement. Some of them, who started with a different language, may choose a path where they go back and learn C, maybe even contribute to projects that require C knowledge. Others may choose a different path where they learn about new languages or techniques. In the end, I think what matters is not what a programmer knows, but what and how quickly he or she can learn, and how well he or she can contribute to solving real problems.

Themocracy WordPress Themes