HTML Dropdown

Sunday, 2 August 2015

How to achieve higher standards of Coding in C#: Part 1


Before knowing about higher standards in coding lets understand the difference between programming and software development. While programming requires a knowledge of computer languages and algorithms and data structures, all of which are very important, software engineering also involves knowledge of projects, maintenance requirements, documentation standards, software design, etc. The things that are involved in successful software projects that are NOT actual programming.
For me, a critical distinction has always come with Software Patterns. I've never met someone who was "just" a programmer who had a grasp on what they are, and why they're good. Conversely, most (albeit not all) Software Engineers that I've known and respected have had a good grasp on Software Patterns, and understand why they're such a good idea.
In general, the way I tend to think of it is this: programmers program solutions. Software engineers take a problem, and (usually) use programming to solve it. The difference is that at least part of the responsibility for figuring out WHAT the solution is going to be falls on the Software Engineers.
This article is not related to any programming language, though the sample is written in C#.  It can be applied to any programming language including HTML, XML, SQL, C++, Java, Python, scripting languages,... 
Introduction
The following is an explanation of the terms "programming"  and "Software Development", as I see it. (They are not necessarily the correct language definitions.) 
Programming 
Creating applications to perform a certain task  (tools). 


Software Development 
Creating professional applications that are easy to use, expandable and easy to change. IOW - well designed.  

That might sound a bit pretentious, which is certainly not my intention.  Let's make that clear by saying that "programming" can be useful, and is not "stupid" IOW you can eg. "program" a very complicated, state-of-the-art algorithm.  However, when providing an application to the public one might think of upgrading or re-writing that programmed application to a software development.  
Let me finish the introduction by saying that we are all guilty of some form of (dirty) programming at some point.  I certainly am not a saint in this regard.  We should however, be aware of this so we can improve if necessary.  
Computer Scientist
They write code (yeah I know it's a bit of a bombshell). It may not be the prettiest or most well-factored code, but it gets the job done. It is not about the design of the code or "good" practices, it is about proving what they set out to prove. A computer scientist is as much a mathematician as they are a technologist (they have 31337 math skills), they don't just need to know that stuff works, they have to prove it. Communication and people skills are desirable traits, but not emphasized. Software process and team dynamics skills are desirable traits, but not emphasized. They have good breadth of general knowledge of their whole field, but they deeply specialize in one or several narrow areas. In these areas they are considered world-class experts. They work on stuff related to their research in their personal time.
Programmer
Programmers write awesome code. Making it clean, well-factored and error free are very important concerns, but not at the expense of getting the job done. It is all about knowing the meaning of "good code" within their domain. They need to have some math skills, but this is not a paramount concern. They need to know of good (best) solutions to problems, but they don't need to prove it is the best solution. A good breadth of algorithmic knowledge is imperative. They have a depth of skill in a wide area of expertise and have reasonably good knowledge of related areas as well. Communication and people skills are desirable traits, but not emphasized. Software process and team dynamics skills are desirable traits, but not emphasized. They work on personal software projects they find of interest in their off time.
Developer
They write code. Making it well-factored and clean is important, but other factors often take priority. Math skills are very much optional, but it does help to be aware of common problems and solutions related to the domain they are in. Communication and people skills are paramount. Process and team dynamics are bread and butter skills. They are consummate generalists without any truly deep specializations. They are expert at finding ways around problems and plugging components together to fulfill a set of requirements. In their personal time they are either trying to build the next Facebook, or engage in activities that have nothing to do with programming, developing, or computer science.
  • Developer are programmers to a greater or lesser extent.
  • Computer scientists are programmers to a greater or lesser extent.
  • Enterprise software is the domain of the developer.
  • The Googles and Microsoft of the world are after programmers (and to a lesser extent computer scientists). The developers who end up there become product managers.
  • RnD and academia are the domain of the computer scientist (and to a lesser extent the programmer)
The thing to remember here is that none of the three is derogatory or "bad" in any way. One is not more or less desirable than any of the others. They are simply different dimensions (with some crossover) of the field we are all involved in. Particular personalities will identify more with one but that does not mean that all three can't "bleed" into each other and combine favorably. It is entirely possible to be both an awesome developer and a great programmer (although it is difficult with so many important things to focus on). In rare cases you may even get an all 3 in 1 type of deal, in which case I'd love to hear from you, cause we should start a company together, so that I can ride your awesomeness all the way to easy-street :). But no matter where you fall, it is entirely possible to be highly successful if you fit snugly into just one of the three.
What about a software engineer? That's just a subset of developer.
What about an architect? They design buildings and stuff, so I am not quite sure how that's relevant :)
Background   
I started thinking about this article when I saw some of the "applications" in our sector and also seeing some of the code snippets.  The real trigger however was when I heard colleagues talk in the hallway, saying that they didn't like software developers because they always work with fancy machines (which the users don't have), they overcomplicate things, ...  Needless to say I was a bit annoyed with this statement (Software developers are obsolete?), certainly because it are these type of people that "program".
A second trigger was when I needed to explain myself to a manager why something took so long or a recruiter on why I am 'better' than the next guy/girl.  
The explanations on why they don't improve the application, are things like "not important", "it does what it's supposed to", "not many will use it", "deadline", ... 
Basic things that I see as "programming" are: 
  • spaghetti code,  
  • no n-tier design, 
  • not changing default naming (button1, button1_Click, ...  ), 
  • no thought on UI design (icons, guide text, consistancy, control location logic ... ), 
  • no thought on UI logic (tab order, mouse clicks, ... )  
  • no thought about extensibility, re-usability, ... 
  • no Exception handling, logging, ...
  • ... 
However, "programming" can be very useful in prototyping or personal projects, you could then strip that application from unnecessary bits, clean it up and incorporate it into a larger Software Development.  (The hard stuff, finding the correct '20' lines of code that build your fancy algorithm is already done)
Using the code
To illustrate what we're trying to clarify we define a request for an application.
Create an application that will add 2 numbers and present the output. 

Before you continue I propose you build this yourself without reading further.  It is a very simple application.  

... building code, ..., building code, ..., building code, ...  
 Done?  Probably you didn't do the above.  No matter, we'll still continue the article.

In this screenshot, you'll see we've met the request.  You enter two numbers and press the add button.  Then it calculates the sum of the two terms and displays it in the third textbox.
So what's wrong with it, just by looking at the screenshot?  
  • controls are not aligned  (subtle enough to be extra annoying)  
  • window title is "Form1" 
  • window icon is the standard visual studio icon 
But the difference between "Programming" and "Software Development" goes further than the obvious:
  • The tab order is all wrong
  • there are no tooltips, no guide text, ... 
  • there's too much empty space on the window and the controls are placed a bit "at random"
  • It can just add numbers, like requested (nothing more) 
  • You can change the actual sum manually  
And now, just for fun, try to provide an alphanumeric character: 


 Oops... 

To compare, we'll check out the "Software Development" version of the same request. 


Here we see that the same request has been met, with the differences:
  • all fields are aligned 
  • You don't need to press a button to perform the calculation (less user action required)
    However, don't overdo this.  I personally hate applications that do too much for me, because it becomes hard to determine what is actually going on. (And is the application doing it correctly?  Didn't I miss something? ...) 
  • there is less empty space  on the window
  • there is some guide text (and not visible, but also tooltips) 
  • not visible, the tab order is logical
  • not visible, added tooltips
  • added icon and changed window title 
  • the solution textbox is read-only and calculated when any of the other textboxes or the combobox are changed  
  • this example allows +, -, x, / and %. If you're argument is, the client doesn't want this, you're right, then build it in and hide the other options for the user.  The point is, if you foresee a future request (eg. please also add subtracting) make sure to foresee to easily modify the code to do this.  
Now let's try to break this:

The second example itself can also be improved, we could provide textboxes that do not allow any alphanumeric characters, we could limit the textbox contents size to a certain number of characters, we could allow operations on currency's, we could allow decimal, octagonal and binary operations and so on and so forth. 
Now we only stated the obvious of what we see on the screen, let's dive deeper into the trenches of our job and have a look at the code. 
The programming example's code looks like this:
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e) {
                textBox3.Text = double.Parse(textBox1.Text) + double.Parse(textBox2.Text) + "";
    }

    private void button2_Click(object sender, EventArgs e) {
                this.Close();
    }
}
Let's sum up what's wrong here: 
  • use of default naming
  • no error checking
  • hardcoded addition
  • no comments  
Now let's have a look at the Software Development code: 
The GUI class  
public partial class BaseMathOperations : Form
{
    public BaseMathOperations()
    {
        InitializeComponent();
                               ToolTip tt = new ToolTip();
                               tt.SetToolTip(txtbox_term1, "fill in term 1.");
                               tt.SetToolTip(txtbox_term2, "fill in term 2.");
                               tt.SetToolTip(txtbox_solution, "the outcome of the math.");
                               tt.SetToolTip(combo_operation, "mathematical operation");
                               tt.SetToolTip(btn_close, "close this application");
                               tt.Active = true;
              
                               combo_operation.SelectedIndex = 0;
               }


    private void txtbox_term_TextChanged(object sender, EventArgs e) {
                               Calculate();
    }

    private void combo_operation_SelectedIndexChanged(object sender, EventArgs e) {
        Calculate();
    }

    private void btn_close_Click(object sender, EventArgs e) {
        this.Close();
    }

    private void Calculate(){
        //check if all three necessary fields are filled in.
                               if(            !string.IsNullOrEmpty(txtbox_term1.Text) &&
                               !string.IsNullOrEmpty(txtbox_term2.Text) &&
                               combo_operation.SelectedIndex > -1){
                                              switch(combo_operation.Text){
                                                             case "+": txtbox_solution.Text = SimpleMath.Add(txtbox_term1.Text, txtbox_term2.Text);
                                                             break;
                                                             case "-": txtbox_solution.Text = SimpleMath.Substract(txtbox_term1.Text, txtbox_term2.Text);
                                                             break;
                                                             case "x": txtbox_solution.Text = SimpleMath.Multiply(txtbox_term1.Text, txtbox_term2.Text);
                                                             break;
                                                             case "/": txtbox_solution.Text = SimpleMath.Divide(txtbox_term1.Text, txtbox_term2.Text);
                                                             break;
                                                             case "%": txtbox_solution.Text = SimpleMath.Mod(txtbox_term1.Text, txtbox_term2.Text);
                                                             break;
                                              }                                                            //end switch                                                   
                               }                                                                           //end if
    }
}
 The SimpleMath class:
public class SimpleMath {
               public static string Add(string term1, string term2){
                               string retval = "";
                               try{
                                              retval = Double2String(String2Double(term1) + String2Double(term2));
                               }                                                            //end try
                               catch(Exception ex){
                                              retval = "not a number";
                               }                                                            //end catch
                               return retval;
               }

               public static string Substract(string term1, string term2){
                               string retval = "";
                               try{
                                              retval = Double2String(String2Double(term1) - String2Double(term2));
                               }                                             //end try
                               catch(Exception ex){
                                              retval = "not a number";
                               }                                             //end catch1
                               return retval;
               }

               public static string Multiply(string term1, string term2){
                               string retval = "";
                               try{
                                              retval = Double2String(String2Double(term1) * String2Double(term2));
                               }                                             //end try
                               catch(Exception ex){
                                              retval = "not a number";
                               }                                             //end catch
                               return retval;
               }

               public static string Divide(string term1, string term2){
                               string retval = "";
                               try{
                                              retval = Double2String(String2Double(term1) / String2Double(term2));
                               }                                             //end try
                               catch(DivideByZeroException dbzex){
                                              retval = "cannot divide by 0";
                               }                                             //end catch
                               catch(Exception ex){
                                              retval = "not a number";
                               }                                             //end catch
                               return retval;
               }

               public static string Mod(string term1, string term2){
                               string retval = "";
                               try{
                                              retval = Double2String(String2Double(term1) % String2Double(term2));
                               }                                             //end try
                               catch(Exception ex){
                                              retval = "not a number";
                               }                                             //end catch
                               return retval;
               }

               private static double String2Double(string val){
                               double retval = double.NaN;
                               if(!double.TryParse(val, out retval)){
                                              retval = double.NaN;
                               }                                             //end if
                               return retval;
               }

               private static string Double2String(double val){
                               string retval = "not a number";
                               if(val != double.NaN){
                                              retval = val.ToString("0.00", CultureInfo.InvariantCulture);
                               }                                             //end if
                               return retval;
               }
}
  
 This code is certainly not perfect, but it is certainly a lot better then the first example. 
  • comments - check
  • error handling - check
  • division of logic (the arithmetic’s are handled by another class)  
  • No default naming - check
  • Better logic in UI design and can handle more than just adding numbers. it also formats the result. 

If you're stating by now that I shouldn't swallow my exceptions you're right.  In the source I added a comment on top of the SimpleMath class:
/*
 *             Remarks:
 *             Normally you don't swallow the Exceptions as done here, but in order to keep the code simple, we don't handle them perse here.
 */ 
So indeed, you should never swallow exceptions, but instead write them to a log file or database and/or bubble them up to the user (in some formatted way).
This second example is certainly longer then the programmed version, but if you look a bit closer, you'll see it is not complex at all.  The SimpleMath class can be reused, we successfully separated GUI and business logic.  (in real applications you would probably split that into different projects).  
In the end you spend time to win time (later on) 

How to achieve higher standards ??

So here are some pointers on "Software Development": 
#1 
Do not start coding directly from a requirement, you'll end up with a program.  Instead take a step back and look at all the angles, design your application (and take time for it):  
  • Will the requirement likely be expanded in a certain direction?
  • Does it need to plugin to another application?
  • What could be reusable? 
  • ...  
#2 
Keep the K.I.S.S. principle.  Don't overcomplicate things.  If you start feeling complexity, start re-thinking.  Don't be scared to re-write bits of your code to simplify the further development.  If you smell trouble coming, fix it before it starts stinking further down the road.   
#3
If you need to implement a difficult algorithm, or want to check some features (get to know how a certain class works) create a new project and use "programming" to create a prototype.  (Although "programming" does not imply you throw all the "software development" rules overboard).  Only when you’re confident in your solution should you incorporate it into the final version.  Doing this directly in the final version will result in a mess rather quickly. 
#4
Clean up code regularly, take time to browse all classes and remove unnecessary comments or scout for complex code that you could do better and easier.  In fact, do so immediately after reading this article. 
#5
Very important: dare to question the requirement and/or your boss ! If you see problems coming or have a good idea you SHOULD communicate this. (IMHO this is what they are paying you for?)  The boss/client normally has the final word, but you can say no, and if they really, really want it, well, you warned them.  
#6 
Test.  You and/or someone else.  You should not test if something works (it probably will), rather try to make it break!  IOW don't test expected behaviour.   eg. You can do this by taking the scenario steps of a user test and modifying them slightly.  That is how I find the most bugs and we all know users never follow the rules on how something should be used. 
#7 
I'm all for defensive programming.  Don't throw try/catch blocks for fun and try to limit out input from the very start developing "safe gates" along the way.  eg.  Integer input can be enforced via the GUI, can be checked in the business - and data access layers and finally can be enforced on the database level.
#8 
As commented below, don't allow changes moments before the deadline.  Setup a proper period of "code freeze" were (almost) no changes are allowed to the code.   Be rigorous about this.  It is even a good period to finalize all the other stuff mostly forgotten: documentation. 
#9
It is good to automate some work for the user, but never forget that a user still remains the responsible person for his/her work.  Never let yourself be drawn in discussions were managers want fully automated systems.  Your code becomes exponentially more complex quicker than an improbability drive can take you to the restaurant.  In many cases the end-users start complaining anyway and many changes will have to be reverted. 
#10
You don't need to adopt every new shiny feature that comes along. 
Conclusion 
#1 
As explained in the background section, you could use the information of this article towards your manager or recruiter.  Furthermore, you can use the information to become more aware of doing things better and certainly simpler.  Finally, be aware that applications can always be approved (take time to review your work later on), it can always be better and don't be too fast on your toes when someone asks you "why didn't you do it that way?" (Probably because you don't or didn't know or there was some reason for it) 
#2
Doing "programming" or "software development" can mean the difference in people using your application or not.  Don't take it too lightly.   
#3

You can take this concept further into research, presentations (!), documentation, etc... 

No comments:

Post a Comment