Groups/colors/removes cells Based on the value of what is in column B (column A is hidden) it groups/colors/removes the corresponding node(s). "R - " values are nodes that need to be grouped. The rows are 6 cells apart before a new node starts. It also adds a formula below the value.

I wrote this code and it of course looks messy. I just want to know if there is a way to make it a little easier to understand.  It's not the best when it comes to recursion and I need to explain this to a few peers so they will be able to keep the model running as well.

Example:



Code for Review:

    'Set values to 5; first row to begin grouping
    Start_Row = 5
    Last_Row = 5

 Application.Wait Now + TimeValue("00:00:02")
 Application.StatusBar = "Grouping and formating workgroups..."

While WorksheetFunction.CountA(Rows(Start_Row)) &lt;&gt; 0

   'Color code certain ranges based on ending values of functions/nodes
   If (Right(range("B" &amp; Start_Row).Value, 10) = "- Overhead" Or Right(range("B" &amp; Start_Row).Value, 14) = "- Non Overhead" Or Right(range("B" &amp; Start_Row).Value, 4) = "- WG") Then
        Finish_Row = Start_Row - 1
        Last_Row = Start_Row + 6
    End If

    With ActiveSheet
    lastRow_das_delete = .Cells(.Rows.Count, "A").End(xlUp).Row
    End With

    'Delete actual and support nodes, loop twice to remove R value rollups
    For double_actuals_support = 0 To lastRow_das_delete
    range("B" &amp; (Start_Row + 1)).Formula = "=CONCATENATE(""~ "" &amp; INDEX('Complete Database Pull'!B:B,MATCH(B" &amp; Start_Row &amp; ",'Complete Database Pull'!C:C,0)))"
    If Right(range("B" &amp; Start_Row).Value, 19) = "- Actuals &amp; Support" Then
        For adjust_delete = 0 To 5
            Rows(Start_Row).EntireRow.Delete
        Next adjust_delete
    End If
    Next double_actuals_support

    'Group R- nodes together
    If Left(range("B" &amp; Start_Row).Value, 2) = "R-" And Start_Row &lt;&gt; 5 And Last_Row &lt;&gt; Start_Row Then
        Finish_Row = Start_Row - 1
        Rows(Finish_Row &amp; ":" &amp; Last_Row).Group
        Last_Row = Start_Row + 6
        'If R- is in the first row, don't group up
    ElseIf Left(range("B" &amp; Start_Row).Value, 2) = "R-" And Start_Row = 5 Then
        Last_Row = Start_Row + 6
    End If

    'Color WG rows/columns
    If (Right(range("B" &amp; Start_Row).Value, 10) = "- Overhead" Or Right(range("B" &amp; Start_Row).Value, 14) = "- Non Overhead") Then
        range("B" &amp; Start_Row &amp; ":Y" &amp; (Start_Row + 3)).Interior.Color = 15853276
        range("B" &amp; Start_Row &amp; ":B" &amp; (Start_Row + 5)).Interior.Color = 15853276
    End If

    If Right(range("B" &amp; Start_Row).Value, 4) = "- WG" Then
        range("B" &amp; Start_Row &amp; ":Y" &amp; (Start_Row + 3)).Interior.Color = 15523812
        range("B" &amp; Start_Row &amp; ":B" &amp; (Start_Row + 5)).Interior.Color = 15523812
        Last_Row = Start_Row + 6
    End If

    'Adjust Start_Row 6 rows
    Start_Row = Start_Row + 6
Wend
Side-scrolling

Don't make do it. If a line of code doesn't fit comfortably inside the code pane of the IDE with a reasonable window size, it's too long. If the code line actually has to be that long, take advantage of VBA's line continuation operator (_) and break it up so you can see the whole line at once. The If tests are especially problematic, given that by the time I've scrolled right far enough to see the last condition I can't see what gets executed if it's true.



Naming

Although the actual naming convention you use is a question of taste, it is good to be consistent. I typically follow a convention similar to the one outlined over on the SO documentation page for Naming Conventions. You have a mix of Pascal and lower snake case for (what appear to be) local variables - i.e. Start_Row and double_actuals_support.

More important is the variables named *_Row. You have Start_Row, Last_Row, and Finish_Row,  These are frankly confusing as to what they are referring to. All of those names imply that they are more or less constant values, but all of them change.  I think of a start row as the first row on a sheet and last row as the last row on the sheet.  Since Start_Row is the holding your loop counter, I'd suggest something more like workingRow or currentRow. Last_Row appears to refer to the last row of each node (it is always offset by 6), so it would be more appropriately named something like nodeEndRow.  Finally Finish_Row appears to be set to the last row of the previous node, so something like previousNodeEnd would be appropriate.



Best Practices

Indentation isn't consistent, but it's hard to tell whether that's an artifact of copying and pasting or not.  You can fix it with Rubberduck's SmartIndenter feature (full disclosure, I'm a contributor).

You should qualify all of your Range and Cells calls to avoid using the global collections (if the code isn't in a worksheet module). Wrap the whole thing in With ActiveSheet (or even better, a worksheet code name like With Sheet1) if it's anywhere other than a worksheet module, otherwise wrap the whole thing in With Me.

Use the String returning versions of functions (they end with $) instead of Variant returning functions when they are available to avoid implicit casts. You can find a list of them here.

Declare your variables and add Option Explicit to your modules.  Otherwise they are implicitly Variant, and this can cause a host of issues. If they are declared somewhere above the code that was posted, they should be moved closer to where they are being used.  For example, double_actuals_support and adjust_delete are what appear to be throw-away loop counters. There's no reason you should need to go somewhere else to see how they're defined.

It's not clear why you call Application.Wait Now + TimeValue("00:00:02") at the start.  If there's a reason you need to do that, comment it.  Also, while updating the status bar is useful, you should immediately call DoEvents after updating it so that the screen can refresh.  Although, it's possible you wont' need it once the row delete issue is addressed, which leads me to...



Skip the row delete gymnastics

I literally had to stare at this section of code for about 5 minutes before I could determine that it probably wasn't actually a bug (and I can still only make an educated guess as to why it's there):


'Delete actual and support nodes, loop twice to remove R value rollups
For double_actuals_support = 0 To lastRow_das_delete
range("B" &amp; (Start_Row + 1)).Formula = "=CONCATENATE(""~ "" &amp; INDEX('Complete Database Pull'!B:B,MATCH(B" &amp; Start_Row &amp; ",'Complete  Database Pull'!C:C,0)))"
If Right(range("B" &amp; Start_Row).Value, 19) = "- Actuals &amp; Support" Then
    For adjust_delete = 0 To 5
        Rows(Start_Row).EntireRow.Delete
    Next adjust_delete
End If
Next double_actuals_support



First, the comment is extremely misleading - this doesn't loop twice, it loops through the every row of the worksheet. As it looks like you found out, deleting rows while you're looping through them alters the row index.  From the above section of code, it appears that you are just reloading the the worksheet from that point down if you have to delete something. This is incredibly inefficient.  

There are 3 much easier and much faster ways to handle this.  Either loop through the rows backwards, accumulate the rows that need to be deleted and delete them all at once when you're done, or conditionally increment your row counter.  If I'm misunderstanding the purpose of this code, you do have a bug, because there isn't anything else in your code that adjusts the indexing for deleted rows.

Note that this is the only thing I could find that indicated "recursion" in the code you posted.  There aren't any function calls and I wouldn't know the name of the posted function anyway.



Avoid repeated function call and Worksheet accesses

If the return value of a function call or property is guaranteed to be identical over multiple calls, you should cache the value in a local variable instead of repeatedly requesting it.  This line is a perfect example (line continuations added for clarity):

 If (Right(range("B" &amp; Start_Row).Value, 10) = "- Overhead" Or _ 
     Right(range("B" &amp; Start_Row).Value, 14) = "- Non Overhead" Or _
     Right(range("B" &amp; Start_Row).Value, 4) = "- WG") Then


The value of Range("B" &amp; Start_Row).Value is going to be exactly the same for the 3 times you're requesting it. This would be much better:

Dim pulled As String
pulled = Range("B", workingRow).Value

If (Right(pulled, 10) = "- Overhead" Or Right(pulled, 14) = "- Non Overhead" Or _
    Right(pulled, 4) = "- WG") Then


Note that your lines are getting shorter now too.



Repeated code

There's a consistent pattern that you repeat over and over again.  Going back to the same line of code as above, you make multiple tests to see if a given string ends with another string similar to this:


If (Right(range("B" &amp; Start_Row).Value, 10) = "- Overhead" ...



I'd throw in a couple simple utility functions called EndsWith and StartsWith and use those instead (StartsWith would be similar):

'Tests to see if a string ends with any of the passed candidates.
Public Function EndsWith(test As String, ParamArray candidates() As Variant) As Boolean
    Dim candidate As Variant
    For Each candidate In candidates
        Dim chars As Long
        chars = Len(candidate)
        If Len(candidate) &lt;= test Then
            If Right$(test, chars) = candidate Then
                EndsWith = True
                Exit Function
            End If
        End If
    Next
End Function


This has several advantages:


It makes the code much clearer as to the intent. 
It gets rid of all of the hard coded string lengths that are prone to errors. 
It almost completely solves the side scrolling issue. 
It short-circuits on the first match (If x Or y Or z doesn't).


The line referenced above can now be something like:

If EndsWith(pulled, "- Overhead", "- Non Overhead", "- WG") Then




Use numeric column indexes and constants

The only reason that Excel has letters for column identifiers is because they are easier for humans to read - it ultimately treats column indexes as a numbers internally. So, it's a (albeit small) performance improvement to use numeric range indexing rather than string indexing because Excel just has to parse it.  Also, for individual cells, using .Cells is much better than using the .Range because Excel doesn't have to do any parsing.

'Good
Range("B", workingRow).Value
'Better
Cells(workingRow, 2).Value


While it's easier to see "B" than to have to mentally calculate that it would be column 2 , it's important to remember that "B" is just as much of a "magic letter" as 2 is a magic number.  You can make code much more readable by defining and using constants for them. This also makes the code tremendously easier to maintain - think about how many places you would need to make changes if your columns all shifted one to the right.  If it's in a constant, you only need to change it in one place:

'Module level
Private Const ROW_INDEX_COLUMN As Long = 1
Private Const DATA_ID_COLUMN As Long = 2
Private Const END_COLUMN As Long = 25
'...
'Best
Cells(workingRow, DATA_ID_COLUMN).Value


Another perfect candidate for a constant would be NODE_LENGTH As Long = 6.

Extract methods for common code

The code used for formatting the cells is pretty much identical other than the colors being used.  Instead of repeating the code twice here...


'Color WG rows/columns
If (Right(range("B" &amp; Start_Row).Value, 10) = "- Overhead" Or Right(range("B" &amp; Start_Row).Value, 14) = "- Non Overhead") Then
    range("B" &amp; Start_Row &amp; ":Y" &amp; (Start_Row + 3)).Interior.Color = 15853276
    range("B" &amp; Start_Row &amp; ":B" &amp; (Start_Row + 5)).Interior.Color = 15853276
End If

If Right(range("B" &amp; Start_Row).Value, 4) = "- WG" Then
    range("B" &amp; Start_Row &amp; ":Y" &amp; (Start_Row + 3)).Interior.Color = 15523812
    range("B" &amp; Start_Row &amp; ":B" &amp; (Start_Row + 5)).Interior.Color = 15523812
    Last_Row = Start_Row + 6
End If



...it would be more readable and maintainable if you made a separate Sub that performed the formatting.  Something like this:

Private Sub FormatDataNode(anchor As Range, colorIndex As Long)
    Dim r As Long, c As Long
    r = anchor.Row
    c = anchor.Column
    With anchor.Parent
        .Range(.Cells(r, c), .Cells(r + 3, END_COLUMN)).Interior.Color = colorIndex
        .Range(.Cells(r, c), .Cells(r + 5, c)).Interior.Color = colorIndex
    End With
End Sub


Then you can simplify the code in the loop to something more like this:

Private Const WG_COLOR As Long = 15523812
Private Const NON_WG_COLOR As Long = 15853276
'...
'Color WG rows/columns
If EndsWith(pulled, "- Overhead", "- Non Overhead") Then
    FormatDataNode Cells(workingRow, DATA_ID_COLUMN), NON_WG_COLOR
ElseIf EndsWith(pulled, "- WG") Then
    FormatDataNode Cells(workingRow, DATA_ID_COLUMN), WG_COLOR
    nodeEndRow = workingRow + NODE_LENGTH
End If




Conclusion

Overall nothing horrible - the suggestions above will make it much more readable.  My guess is that taking a different strategy to deleting rows will have a huge impact in both performance and correctness.  After that's sorted out, my guess is that this code can be simplified tremendously - feel free to post a follow-up question after your first refactoring round.