Chain of pd.Series easy to understand, also if you would like know more methods ,check unnesting 
df.set_index(['name','id']).apps.apply(pd.Series).\
         stack().apply(pd.Series).\
            reset_index(level=[0,1]).\
                rename(columns={0:'app_name',1:'app_version'})
Out[541]: 
    name  id app_name app_version
0   john   1     app1          v1
1   john   1     app2          v2
2   john   1     app3          v3
0  smith   2     app1          v1
1  smith   2     app4          v4
Method two slightly modify the function I write 
def unnesting(df, explode):
    idx = df.index.repeat(df[explode[0]].str.len())
    df1 = pd.concat([
        pd.DataFrame({x: sum(df[x].tolist(),[])}) for x in explode], axis=1)
    df1.index = idx
    return df1.join(df.drop(explode, 1), how='left')
Then 
yourdf=unnesting(df,['apps'])
yourdf['app_name'],yourdf['app_version']=yourdf.apps.str[0],yourdf.apps.str[1]
yourdf
Out[548]: 
         apps  id   name app_name app_version
0  [app1, v1]   1   john     app1          v1
0  [app2, v2]   1   john     app2          v2
0  [app3, v3]   1   john     app3          v3
1  [app1, v1]   2  smith     app1          v1
1  [app4, v4]   2  smith     app4          v4
Or 
yourdf=unnesting(df,['apps']).reindex(columns=df.columns.tolist()+['app_name','app_version'])
yourdf[['app_name','app_version']]=yourdf.apps.tolist()
yourdf
Out[567]: 
         apps  id   name app_name app_version
0  [app1, v1]   1   john     app1          v1
0  [app2, v2]   1   john     app2          v2
0  [app3, v3]   1   john     app3          v3
1  [app1, v1]   2  smith     app1          v1
1  [app4, v4]   2  smith     app4          v4