Refactoring in software development is a very non-transparent thing for non-technical people. Especially big refactorings leave stakeholders like product owners or managers in uncertainty about the process of (well-needed) technical improvements.
In this blog post, I want to sketch an idea how we as developer could possibly communicate the work "under the hood" by using visualization.
We use jQAssistant/Neo4j and Pandas/pygal to report the status of technical improvements.
Thant to tools like jQAssistant, we have access to the underlying structures of our software. Almost every entity our software system consists of is connected with further entities via a relationship:
and so on. All this data is scanned by jQAssistant and stored into the Neo4j graph database.
We can also associate problems (or "refactoring opportunities") with the entities of our software system. E. g. we can connect finding from FindBugs to classes, detect oven changing but buggy code or even create our own code smell detection algorithms for spotting ugly race conditions in our software.
In [68]:
import py2neo
import pandas as pd
graph = py2neo.Graph()
query = """
MATCH (c:Class)-[:DECLARES]->(m:Method)
WHERE
c.fqn STARTS WITH "org.springframework.samples.petclinic"
OPTIONAL MATCH
(c)-[r:DEPENDS_ON]->
(dbType:Type)-[:EXTENDS]->
(base:Class {name: "BaseEntity"})
RETURN
c.fqn as fqn,
c.fqn as class,
COUNT(DISTINCT dbType.fqn) as count,
MAX(m.lastLineNumber) as lines
ORDER BY fqn
"""
entity_dependencies = pd.DataFrame(graph.data(query))
entity_dependencies.head()
Out[68]:
In [69]:
TEST_COLOR_CODE = 'rgba(0,0,255,{})'
CODE_COLOR_CODE = 'rgba(255,0,0,{})'
OTHER_COLOR_CODE = 'lightgrey'
plot_data = entity_dependencies.copy()
plot_data['ratio'] = plot_data['count'] / plot_data['count'].max()
plot_data.loc[plot_data['class'].str.endswith("Test"), 'color_code'] = \
TEST_COLOR_CODE.format(plot_data['ratio'])
plot_data.loc[plot_data['class'].str.endswith("Test"), 'color_code'] = \
CODE_COLOR_CODE.format(plot_data['ratio'])
plot_data['color'] = plot_data['color'].fillna(OTHER_COLOR_CODE)
plot_data.head()
In [74]:
import pygal
treemap = pygal.Treemap(
pygal.Config(show_legend=False))
max_count = entity_dependencies['count'].max()
for row in entity_dependencies.iterrows():
entry = row[1]
ratio = entry['count'] / max_count
data = {}
data['value'] = entry['lines']
data['color'] = 'lightgrey' \
if ratio == 0 else 'rgba(255,0,0,{})'.format(str(ratio))
data['label'] = entry['class']
treemap.add(entry['class'], [data])
treemap.render_in_browser()
Dann noch commits zu behebungsticket reinnehmen